This commit is contained in:
parent
d21432dad7
commit
e1eaf964f6
|
|
@ -44,11 +44,13 @@ export default function AnalysisPage() {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
<div className="max-w-6xl mx-auto space-y-8">
|
||||
<div className="min-h-screen bg-gradient-to-br from-blue-50 via-purple-50 to-pink-50 dark:from-gray-900 dark:via-purple-900/20 dark:to-blue-900/20">
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
<div className="max-w-6xl mx-auto space-y-8">
|
||||
{/* 標題區域 - 置中對齊 */}
|
||||
<div className="text-center">
|
||||
<h1 className="text-4xl font-bold mb-2">交易分析</h1>
|
||||
<div className="text-center relative">
|
||||
<div className="absolute inset-0 gradient-bg-radial opacity-40 -z-10" />
|
||||
<h1 className="text-4xl font-bold mb-2 gradient-text-primary">交易分析</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
配置並執行全面的多代理交易分析
|
||||
</p>
|
||||
|
|
@ -66,6 +68,7 @@ export default function AnalysisPage() {
|
|||
<p className="text-red-600 dark:text-red-400">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -129,12 +129,14 @@ export default function AnalysisResultsPage() {
|
|||
const currentReport = getNestedValue(analysisResult.reports, currentAnalyst?.reportKey || "");
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
<div className="max-w-7xl mx-auto space-y-8">
|
||||
<div className="min-h-screen bg-gradient-to-br from-blue-50 via-purple-50 to-pink-50 dark:from-gray-900 dark:via-purple-900/20 dark:to-blue-900/20">
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
<div className="max-w-7xl mx-auto space-y-8">
|
||||
{/* Header */}
|
||||
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4 animate-fade-in">
|
||||
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4 animate-fade-in relative">
|
||||
<div className="absolute inset-0 gradient-bg-radial opacity-30 -z-10 rounded-lg" />
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold mb-2">
|
||||
<h1 className="text-4xl font-bold mb-2 gradient-text-primary">
|
||||
{analysisResult.ticker} 詳細分析結果
|
||||
</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
|
|
@ -178,7 +180,7 @@ export default function AnalysisResultsPage() {
|
|||
)}
|
||||
|
||||
{/* 分析師報告 */}
|
||||
<Card className="animate-scale-up hover-lift">
|
||||
<Card className="animate-scale-up hover-lift gradient-card gradient-shine">
|
||||
<CardHeader>
|
||||
<CardTitle>{analyst.label} 報告</CardTitle>
|
||||
<CardDescription>
|
||||
|
|
@ -219,6 +221,7 @@ export default function AnalysisResultsPage() {
|
|||
reports={analysisResult.reports}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -252,3 +252,282 @@
|
|||
background-size: 200% auto;
|
||||
animation: shimmer 3s linear infinite;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
GRADIENT UTILITIES
|
||||
======================================== */
|
||||
|
||||
/* Gradient Backgrounds */
|
||||
.gradient-bg-primary {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
.dark .gradient-bg-primary {
|
||||
background: linear-gradient(135deg, #4c51bf 0%, #553c9a 100%);
|
||||
}
|
||||
|
||||
.gradient-bg-secondary {
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
}
|
||||
|
||||
.dark .gradient-bg-secondary {
|
||||
background: linear-gradient(135deg, #c471ed 0%, #c94b4b 100%);
|
||||
}
|
||||
|
||||
.gradient-bg-accent {
|
||||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||
}
|
||||
|
||||
.dark .gradient-bg-accent {
|
||||
background: linear-gradient(135deg, #3b82f6 0%, #06b6d4 100%);
|
||||
}
|
||||
|
||||
.gradient-bg-radial {
|
||||
background: radial-gradient(
|
||||
circle at top right,
|
||||
rgba(102, 126, 234, 0.15),
|
||||
transparent 50%
|
||||
),
|
||||
radial-gradient(
|
||||
circle at bottom left,
|
||||
rgba(118, 75, 162, 0.15),
|
||||
transparent 50%
|
||||
);
|
||||
}
|
||||
|
||||
.dark .gradient-bg-radial {
|
||||
background: radial-gradient(
|
||||
circle at top right,
|
||||
rgba(76, 81, 191, 0.2),
|
||||
transparent 50%
|
||||
),
|
||||
radial-gradient(
|
||||
circle at bottom left,
|
||||
rgba(85, 60, 154, 0.2),
|
||||
transparent 50%
|
||||
);
|
||||
}
|
||||
|
||||
.gradient-bg-mesh {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
#667eea 0%,
|
||||
#764ba2 25%,
|
||||
#f093fb 50%,
|
||||
#f5576c 75%,
|
||||
#4facfe 100%
|
||||
);
|
||||
background-size: 400% 400%;
|
||||
animation: gradientShift 15s ease infinite;
|
||||
}
|
||||
|
||||
.dark .gradient-bg-mesh {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
#4c51bf 0%,
|
||||
#553c9a 25%,
|
||||
#c471ed 50%,
|
||||
#c94b4b 75%,
|
||||
#3b82f6 100%
|
||||
);
|
||||
background-size: 400% 400%;
|
||||
animation: gradientShift 15s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes gradientShift {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Gradient Page Backgrounds - Enhanced visibility */
|
||||
.gradient-page-bg {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(102, 126, 234, 0.12) 0%,
|
||||
rgba(240, 242, 255, 0.8) 25%,
|
||||
rgba(255, 240, 245, 0.8) 50%,
|
||||
rgba(245, 240, 255, 0.8) 75%,
|
||||
rgba(118, 75, 162, 0.12) 100%
|
||||
);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.dark .gradient-page-bg {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(76, 81, 191, 0.25) 0%,
|
||||
rgba(30, 30, 45, 0.95) 25%,
|
||||
rgba(40, 30, 45, 0.95) 50%,
|
||||
rgba(35, 30, 50, 0.95) 75%,
|
||||
rgba(85, 60, 154, 0.25) 100%
|
||||
);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* Gradient Borders */
|
||||
.gradient-border {
|
||||
position: relative;
|
||||
border: 2px solid transparent;
|
||||
background: linear-gradient(var(--background), var(--background)) padding-box,
|
||||
linear-gradient(135deg, #667eea, #764ba2) border-box;
|
||||
}
|
||||
|
||||
.dark .gradient-border {
|
||||
background: linear-gradient(var(--background), var(--background)) padding-box,
|
||||
linear-gradient(135deg, #4c51bf, #553c9a) border-box;
|
||||
}
|
||||
|
||||
.gradient-border-animated {
|
||||
position: relative;
|
||||
border: 2px solid transparent;
|
||||
background: linear-gradient(var(--background), var(--background)) padding-box,
|
||||
linear-gradient(135deg, #667eea, #764ba2, #f093fb, #667eea) border-box;
|
||||
background-size: 300% 300%;
|
||||
animation: gradientBorder 4s ease infinite;
|
||||
}
|
||||
|
||||
.dark .gradient-border-animated {
|
||||
background: linear-gradient(var(--background), var(--background)) padding-box,
|
||||
linear-gradient(135deg, #4c51bf, #553c9a, #c471ed, #4c51bf) border-box;
|
||||
background-size: 300% 300%;
|
||||
animation: gradientBorder 4s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes gradientBorder {
|
||||
0%,
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Gradient Text */
|
||||
.gradient-text-primary {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.gradient-text-secondary {
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.gradient-text-accent {
|
||||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
/* Gradient Overlays */
|
||||
.gradient-overlay {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.gradient-overlay::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(102, 126, 234, 0.1),
|
||||
rgba(118, 75, 162, 0.1)
|
||||
);
|
||||
pointer-events: none;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.dark .gradient-overlay::before {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(76, 81, 191, 0.15),
|
||||
rgba(85, 60, 154, 0.15)
|
||||
);
|
||||
}
|
||||
|
||||
/* Gradient Shine Effect */
|
||||
.gradient-shine {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gradient-shine::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: linear-gradient(
|
||||
45deg,
|
||||
transparent 30%,
|
||||
rgba(255, 255, 255, 0.1) 50%,
|
||||
transparent 70%
|
||||
);
|
||||
transform: translateX(-100%) translateY(-100%);
|
||||
transition: transform 0.6s;
|
||||
}
|
||||
|
||||
.gradient-shine:hover::after {
|
||||
transform: translateX(100%) translateY(100%);
|
||||
}
|
||||
|
||||
.dark .gradient-shine::after {
|
||||
background: linear-gradient(
|
||||
45deg,
|
||||
transparent 30%,
|
||||
rgba(255, 255, 255, 0.05) 50%,
|
||||
transparent 70%
|
||||
);
|
||||
}
|
||||
|
||||
/* Enhanced Card Gradients */
|
||||
.gradient-card {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(102, 126, 234, 0.05) 0%,
|
||||
rgba(118, 75, 162, 0.05) 100%
|
||||
);
|
||||
border: 1px solid rgba(102, 126, 234, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.gradient-card:hover {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(102, 126, 234, 0.1) 0%,
|
||||
rgba(118, 75, 162, 0.1) 100%
|
||||
);
|
||||
border-color: rgba(102, 126, 234, 0.4);
|
||||
box-shadow: 0 10px 40px rgba(102, 126, 234, 0.2);
|
||||
}
|
||||
|
||||
.dark .gradient-card {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(76, 81, 191, 0.1) 0%,
|
||||
rgba(85, 60, 154, 0.1) 100%
|
||||
);
|
||||
border-color: rgba(76, 81, 191, 0.3);
|
||||
}
|
||||
|
||||
.dark .gradient-card:hover {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(76, 81, 191, 0.15) 0%,
|
||||
rgba(85, 60, 154, 0.15) 100%
|
||||
);
|
||||
border-color: rgba(76, 81, 191, 0.5);
|
||||
box-shadow: 0 10px 40px rgba(76, 81, 191, 0.3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export default function RootLayout({
|
|||
<body className={inter.className}>
|
||||
<ThemeProvider>
|
||||
<AnalysisProvider>
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<div className="flex flex-col min-h-screen gradient-page-bg">
|
||||
<Header />
|
||||
<main className="flex-1">{children}</main>
|
||||
<Footer />
|
||||
|
|
|
|||
|
|
@ -11,10 +11,12 @@ import {
|
|||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
{/* Hero Section */}
|
||||
<div className="text-center mb-16 animate-fade-in">
|
||||
<h1 className="text-5xl font-bold mb-4 bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
|
||||
<div className="min-h-screen bg-gradient-to-br from-blue-50 via-purple-50 to-pink-50 dark:from-gray-900 dark:via-purple-900/20 dark:to-blue-900/20">
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
{/* Hero Section */}
|
||||
<div className="text-center mb-16 animate-fade-in relative py-8">
|
||||
<div className="absolute inset-0 gradient-bg-radial -z-10" />
|
||||
<h1 className="text-5xl md:text-6xl font-bold mb-6 gradient-text-primary leading-tight">
|
||||
TradingAgentsX
|
||||
</h1>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-400 mb-8 max-w-2xl mx-auto">
|
||||
|
|
@ -24,7 +26,7 @@ export default function HomePage() {
|
|||
<Link href="/analysis">
|
||||
<Button
|
||||
size="lg"
|
||||
className="bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700"
|
||||
className="bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 shadow-lg hover:shadow-xl transition-shadow"
|
||||
>
|
||||
開始分析
|
||||
</Button>
|
||||
|
|
@ -324,20 +326,22 @@ export default function HomePage() {
|
|||
</div>
|
||||
|
||||
{/* Call to Action Section */}
|
||||
<div className="text-center py-16">
|
||||
<h2 className="text-3xl font-bold mb-4">準備好開始智能交易分析了嗎?</h2>
|
||||
<div className="text-center py-16 relative">
|
||||
<div className="absolute inset-0 gradient-bg-radial opacity-60 -z-10" />
|
||||
<h2 className="text-3xl font-bold mb-4 gradient-text-primary">準備好開始智能交易分析了嗎?</h2>
|
||||
<p className="text-lg text-gray-600 dark:text-gray-400 mb-8 max-w-2xl mx-auto">
|
||||
立即體驗 12 位專業 AI 代理協同工作,為您提供全方位的股票分析報告
|
||||
</p>
|
||||
<Link href="/analysis">
|
||||
<Button
|
||||
size="lg"
|
||||
className="bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 text-lg px-8 py-6"
|
||||
className="bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 text-lg px-8 py-6 shadow-lg hover:shadow-2xl transition-all"
|
||||
>
|
||||
開始分析 →
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -352,7 +356,7 @@ function FeatureCard({
|
|||
icon: string;
|
||||
}) {
|
||||
return (
|
||||
<Card className="hover-lift animate-slide-up">
|
||||
<Card className="hover-lift animate-slide-up gradient-shine gradient-card">
|
||||
<CardHeader>
|
||||
<div className="text-4xl mb-2">{icon}</div>
|
||||
<CardTitle className="text-lg">{title}</CardTitle>
|
||||
|
|
@ -376,7 +380,7 @@ function AgentCard({
|
|||
responsibilities: string[];
|
||||
}) {
|
||||
return (
|
||||
<Card className="hover-lift animate-scale-up">
|
||||
<Card className="hover-lift animate-scale-up gradient-card">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">{name}</CardTitle>
|
||||
<CardDescription className="text-xs">{role}</CardDescription>
|
||||
|
|
@ -417,7 +421,7 @@ function LLMProviderCard({
|
|||
const logoSrc = logoMap[name];
|
||||
|
||||
return (
|
||||
<Card className="hover-lift animate-slide-up animate-delay-100">
|
||||
<Card className="hover-lift animate-slide-up animate-delay-100 gradient-card gradient-shine">
|
||||
<CardHeader>
|
||||
<div className="flex items-center gap-3">
|
||||
{logoSrc ? (
|
||||
|
|
@ -458,7 +462,7 @@ function TechnicalCard({
|
|||
features: string[];
|
||||
}) {
|
||||
return (
|
||||
<Card className="hover-lift animate-slide-up animate-delay-300">
|
||||
<Card className="hover-lift animate-slide-up animate-delay-300 gradient-card">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg">{title}</CardTitle>
|
||||
</CardHeader>
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ export function PriceChart({ priceData, priceStats, ticker }: PriceChartProps) {
|
|||
/>
|
||||
</LineChart>
|
||||
) : (
|
||||
// 真正的K線圖(蠟燭圖)
|
||||
<ComposedChart data={priceData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis
|
||||
|
|
@ -121,41 +122,89 @@ export function PriceChart({ priceData, priceStats, ticker }: PriceChartProps) {
|
|||
tickFormatter={(value) => `$${value.toFixed(0)}`}
|
||||
/>
|
||||
<Tooltip
|
||||
formatter={(value: number, name: string) => [`$${formatNumber(value)}`, name]}
|
||||
labelFormatter={(label) => `日期: ${label}`}
|
||||
content={({ active, payload }) => {
|
||||
if (active && payload && payload.length) {
|
||||
const data = payload[0].payload;
|
||||
return (
|
||||
<div className="bg-background border border-border p-3 rounded-lg shadow-lg">
|
||||
<p className="text-sm font-semibold mb-2">日期: {data.Date}</p>
|
||||
<div className="space-y-1 text-sm">
|
||||
<p className="text-green-600">開: ${formatNumber(data.Open)}</p>
|
||||
<p className="text-red-600">收: ${formatNumber(data.Close)}</p>
|
||||
<p className="text-blue-600">高: ${formatNumber(data.High)}</p>
|
||||
<p className="text-orange-600">低: ${formatNumber(data.Low)}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
/>
|
||||
<Legend />
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="High"
|
||||
stroke="#86efac"
|
||||
fill="#86efac"
|
||||
fillOpacity={0.3}
|
||||
name="最高價"
|
||||
/>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="Low"
|
||||
stroke="#fca5a5"
|
||||
fill="#fca5a5"
|
||||
fillOpacity={0.3}
|
||||
name="最低價"
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="Open"
|
||||
stroke="#f59e0b"
|
||||
strokeWidth={2}
|
||||
name="開盤價"
|
||||
dot={false}
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="Close"
|
||||
stroke="#2563eb"
|
||||
strokeWidth={2}
|
||||
name="收盤價"
|
||||
dot={false}
|
||||
<Bar
|
||||
dataKey="Close"
|
||||
shape={(props: any) => {
|
||||
const { x, y, width, payload } = props;
|
||||
const { Open, Close, High, Low } = payload;
|
||||
|
||||
// 計算Y軸的比例尺
|
||||
const yAxis = props.yAxis;
|
||||
const yScale = (value: number) => {
|
||||
const { domain, height } = yAxis;
|
||||
const [min, max] = domain;
|
||||
return height - ((value - min) / (max - min)) * height;
|
||||
};
|
||||
|
||||
const openY = yScale(Open);
|
||||
const closeY = yScale(Close);
|
||||
const highY = yScale(High);
|
||||
const lowY = yScale(Low);
|
||||
|
||||
// 判斷漲跌
|
||||
const isUp = Close >= Open;
|
||||
const color = isUp ? '#22c55e' : '#ef4444'; // 綠色上漲,紅色下跌
|
||||
const fillColor = isUp ? '#22c55e' : '#ef4444';
|
||||
|
||||
// K線寬度
|
||||
const candleWidth = Math.min(width * 0.6, 8);
|
||||
const centerX = x + width / 2;
|
||||
|
||||
// 實體高度
|
||||
const bodyHeight = Math.abs(closeY - openY);
|
||||
const bodyY = Math.min(openY, closeY);
|
||||
|
||||
return (
|
||||
<g>
|
||||
{/* 上影線 */}
|
||||
<line
|
||||
x1={centerX}
|
||||
y1={highY}
|
||||
x2={centerX}
|
||||
y2={Math.min(openY, closeY)}
|
||||
stroke={color}
|
||||
strokeWidth={1}
|
||||
/>
|
||||
{/* 下影線 */}
|
||||
<line
|
||||
x1={centerX}
|
||||
y1={Math.max(openY, closeY)}
|
||||
x2={centerX}
|
||||
y2={lowY}
|
||||
stroke={color}
|
||||
strokeWidth={1}
|
||||
/>
|
||||
{/* K線實體 */}
|
||||
<rect
|
||||
x={centerX - candleWidth / 2}
|
||||
y={bodyY}
|
||||
width={candleWidth}
|
||||
height={bodyHeight || 1} // 至少1px高度避免十字星消失
|
||||
fill={fillColor}
|
||||
stroke={color}
|
||||
strokeWidth={1}
|
||||
/>
|
||||
</g>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</ComposedChart>
|
||||
)}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue