Files
ai-showcase-platform/components/admin/analytics-dashboard.tsx

822 lines
32 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
BarChart,
Bar,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
PieChart,
Pie,
Cell,
Line,
ComposedChart,
} from "recharts"
import { Users, Eye, Star, TrendingUp, Clock, Activity, Calendar, AlertTriangle, Loader2 } from "lucide-react"
import { useState, useEffect } from "react"
export function AnalyticsDashboard() {
const [showHistoryModal, setShowHistoryModal] = useState(false)
const [selectedDateRange, setSelectedDateRange] = useState("近7天")
const [isLoading, setIsLoading] = useState(true)
const [analyticsData, setAnalyticsData] = useState({
totalUsers: 0,
todayActiveUsers: 0,
todayActiveGrowth: 0,
avgRating: 0,
ratingGrowth: 0,
totalApps: 0,
newThisWeek: 0,
userGrowth: 0,
userGrowthText: "較上月",
dailyUsageData: [],
categoryData: [],
topApps: [],
hourlyData: [],
satisfactionRate: 0,
weeklyFeedback: 0,
userAvgRating: 0,
totalRatings: 0,
systemLoadStatus: "normal",
systemLoadAdvice: "",
maxCpuPeak: 0,
maxDailyUsers: 0,
avgDailyUsers: 0,
totalWeeklySessions: 0,
hourlyAnalysis: "",
hourlyAdvice: ""
})
// 載入分析數據
const loadAnalyticsData = async () => {
try {
setIsLoading(true)
console.log('🔄 開始載入分析數據...')
const response = await fetch('/api/admin/analytics')
const data = await response.json()
console.log('📊 API回應數據:', data)
if (data.success) {
setAnalyticsData(data.data)
console.log('✅ 分析數據載入成功')
} else {
console.error('❌ 載入分析數據失敗:', data.error)
}
} catch (error) {
console.error('❌ 載入分析數據錯誤:', error)
} finally {
setIsLoading(false)
}
}
// 初始載入數據
useEffect(() => {
loadAnalyticsData()
}, [])
// 使用API提供的24小時使用數據
const hourlyData = analyticsData.hourlyData || []
// 獲取顏色基於使用強度
const getBarColor = (intensity: string) => {
switch (intensity) {
case "peak":
return "#ef4444" // 紅色 - 高峰期
case "high":
return "#3b82f6" // 藍色 - 高使用期
case "normal":
return "#6b7280" // 灰藍色 - 正常期
case "low":
return "#9ca3af" // 灰色 - 低峰期
default:
return "#6b7280"
}
}
// 近7天使用趨勢數據動態日期
const getRecentDates = () => {
const dates = []
const today = new Date()
for (let i = 6; i >= 0; i--) {
const date = new Date(today)
date.setDate(today.getDate() - i)
dates.push({
date: `${date.getMonth() + 1}/${date.getDate()}`,
fullDate: date.toLocaleDateString("zh-TW"),
dayName: ["日", "一", "二", "三", "四", "五", "六"][date.getDay()],
})
}
return dates
}
const recentDates = getRecentDates()
// 使用API提供的真實數據如果沒有則使用默認數據
const dailyUsageData = analyticsData.dailyUsageData && analyticsData.dailyUsageData.length > 0
? analyticsData.dailyUsageData
: [
{ ...recentDates[0], users: 0, sessions: 0, cpuPeak: 20, avgCpu: 15, memoryPeak: 25, requests: 0 },
{ ...recentDates[1], users: 0, sessions: 0, cpuPeak: 20, avgCpu: 15, memoryPeak: 25, requests: 0 },
{ ...recentDates[2], users: 0, sessions: 0, cpuPeak: 20, avgCpu: 15, memoryPeak: 25, requests: 0 },
{ ...recentDates[3], users: 0, sessions: 0, cpuPeak: 20, avgCpu: 15, memoryPeak: 25, requests: 0 },
{ ...recentDates[4], users: 0, sessions: 0, cpuPeak: 20, avgCpu: 15, memoryPeak: 25, requests: 0 },
{ ...recentDates[5], users: 0, sessions: 0, cpuPeak: 20, avgCpu: 15, memoryPeak: 25, requests: 0 },
{ ...recentDates[6], users: 0, sessions: 0, cpuPeak: 20, avgCpu: 15, memoryPeak: 25, requests: 0 },
]
// 使用API提供的真實類別數據如果沒有則使用默認數據
const categoryData = analyticsData.categoryData && analyticsData.categoryData.length > 0
? analyticsData.categoryData
: [
{ name: "AI工具", value: 35, color: "#3b82f6", users: 0, apps: 0 },
{ name: "數據分析", value: 25, color: "#ef4444", users: 0, apps: 0 },
{ name: "自動化", value: 20, color: "#10b981", users: 0, apps: 0 },
{ name: "機器學習", value: 15, color: "#f59e0b", users: 0, apps: 0 },
{ name: "其他", value: 5, color: "#8b5cf6", users: 0, apps: 0 },
]
const topApps = [
{ name: "智能客服助手", views: 1234, rating: 4.8, category: "AI工具" },
{ name: "數據視覺化平台", views: 987, rating: 4.6, category: "數據分析" },
{ name: "自動化工作流", views: 856, rating: 4.7, category: "自動化" },
{ name: "預測分析系統", views: 743, rating: 4.5, category: "機器學習" },
{ name: "文本分析工具", views: 692, rating: 4.4, category: "AI工具" },
]
// 獲取歷史數據 - 使用真實數據
const getHistoricalData = (range: string) => {
// 所有時間範圍都使用真實的 dailyUsageData
// 因為我們已經在API中獲取了近7天的真實數據
return analyticsData.dailyUsageData || []
}
// 獲取歷史統計數據 - 使用真實數據
const getHistoricalStats = (range: string) => {
const data = getHistoricalData(range)
if (!data || data.length === 0) {
return {
avgUsers: 0,
maxUsers: 0,
avgCpu: 0,
maxCpu: 0,
}
}
const users = data.map((d) => d.users || 0)
const cpus = data.map((d) => d.cpuPeak || 0)
return {
avgUsers: Math.round(users.reduce((a, b) => a + b, 0) / users.length),
maxUsers: Math.max(...users),
avgCpu: Math.round(cpus.reduce((a, b) => a + b, 0) / cpus.length),
maxCpu: Math.max(...cpus),
}
}
return (
<div className="p-6 space-y-6">
<div className="flex items-center justify-between">
<h1 className="text-3xl font-bold"></h1>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
onClick={loadAnalyticsData}
disabled={isLoading}
>
{isLoading ? (
<Loader2 className="w-4 h-4 mr-1 animate-spin" />
) : (
<Activity className="w-4 h-4 mr-1" />
)}
</Button>
</div>
</div>
{/* 關鍵指標卡片 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium"></CardTitle>
<Users className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">
{isLoading ? (
<Loader2 className="w-6 h-6 animate-spin" />
) : (
analyticsData.totalUsers.toLocaleString()
)}
</div>
<p className="text-xs text-muted-foreground">
{isLoading ? (
"載入中..."
) : (
<>
<span className="text-green-600">+{analyticsData.userGrowth}%</span> {analyticsData.userGrowthText}
</>
)}
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium"></CardTitle>
<Eye className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">
{isLoading ? (
<Loader2 className="w-6 h-6 animate-spin" />
) : (
analyticsData.todayActiveUsers
)}
</div>
<p className="text-xs text-muted-foreground">
{isLoading ? (
"載入中..."
) : (
<>
<span className="text-green-600">+{analyticsData.todayActiveGrowth}%</span>
</>
)}
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium"></CardTitle>
<Star className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">
{isLoading ? (
<Loader2 className="w-6 h-6 animate-spin" />
) : (
analyticsData.avgRating
)}
</div>
<p className="text-xs text-muted-foreground">
{isLoading ? (
"載入中..."
) : (
<>
<span className="text-green-600">+{analyticsData.ratingGrowth}</span>
</>
)}
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium"></CardTitle>
<TrendingUp className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">
{isLoading ? (
<Loader2 className="w-6 h-6 animate-spin" />
) : (
analyticsData.totalApps
)}
</div>
<p className="text-xs text-muted-foreground">
{isLoading ? (
"載入中..."
) : (
<>
<span className="text-green-600">+{analyticsData.newThisWeek}</span>
</>
)}
</p>
</CardContent>
</Card>
</div>
{/* 圖表區域 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* 近7天使用趨勢與系統負載 */}
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle className="flex items-center gap-2">
<TrendingUp className="w-5 h-5" />
7使
</CardTitle>
<Button variant="outline" size="sm" onClick={() => setShowHistoryModal(true)}>
<Calendar className="w-4 h-4 mr-1" />
</Button>
</div>
<p className="text-sm text-muted-foreground">CPU使用率關聯分析</p>
</CardHeader>
<CardContent>
<ResponsiveContainer width="100%" height={320}>
<ComposedChart data={analyticsData.dailyUsageData}>
<defs>
<linearGradient id="colorUsers" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#3b82f6" stopOpacity={0.3} />
<stop offset="95%" stopColor="#3b82f6" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" />
<XAxis
dataKey="date"
tick={{ fontSize: 12 }}
axisLine={{ stroke: "#e5e7eb" }}
tickLine={{ stroke: "#e5e7eb" }}
/>
<YAxis
yAxisId="users"
orientation="left"
tick={{ fontSize: 12 }}
axisLine={{ stroke: "#e5e7eb" }}
tickLine={{ stroke: "#e5e7eb" }}
domain={[200, 400]}
/>
<YAxis
yAxisId="cpu"
orientation="right"
tick={{ fontSize: 12 }}
axisLine={{ stroke: "#e5e7eb" }}
tickLine={{ stroke: "#e5e7eb" }}
domain={[40, 90]}
/>
<Tooltip
formatter={(value, name, props) => {
if (name === "users") {
return [`${value}`, "活躍用戶"]
}
if (name === "cpuPeak") {
return [`${value}%`, "CPU峰值"]
}
return [value, name]
}}
labelFormatter={(label, payload) => {
if (payload && payload.length > 0) {
const data = payload[0].payload
return `${data.fullDate} (週${data.dayName})`
}
return label
}}
contentStyle={{
backgroundColor: "white",
border: "1px solid #e5e7eb",
borderRadius: "8px",
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
fontSize: "14px",
}}
/>
<Bar yAxisId="users" dataKey="users" fill="url(#colorUsers)" radius={[4, 4, 0, 0]} opacity={0.7} />
<Line
yAxisId="cpu"
type="monotone"
dataKey="cpuPeak"
stroke="#ef4444"
strokeWidth={3}
dot={{ fill: "#ef4444", strokeWidth: 2, r: 5 }}
activeDot={{ r: 7, stroke: "#ef4444", strokeWidth: 2 }}
/>
</ComposedChart>
</ResponsiveContainer>
{/* 系統建議 - 動態狀態 */}
<div className={`mt-4 p-3 rounded-lg border ${
analyticsData.systemLoadStatus === 'critical'
? 'bg-red-50 border-red-200'
: analyticsData.systemLoadStatus === 'warning'
? 'bg-orange-50 border-orange-200'
: analyticsData.systemLoadStatus === 'monitor'
? 'bg-blue-50 border-blue-200'
: analyticsData.systemLoadStatus === 'low'
? 'bg-yellow-50 border-yellow-200'
: 'bg-gray-50 border-gray-200'
}`}>
<div className="flex items-start gap-2">
{analyticsData.systemLoadStatus === 'critical' ? (
<AlertTriangle className="w-5 h-5 text-red-600 mt-0.5 flex-shrink-0" />
) : analyticsData.systemLoadStatus === 'warning' ? (
<AlertTriangle className="w-5 h-5 text-orange-600 mt-0.5 flex-shrink-0" />
) : analyticsData.systemLoadStatus === 'monitor' ? (
<Activity className="w-5 h-5 text-blue-600 mt-0.5 flex-shrink-0" />
) : analyticsData.systemLoadStatus === 'low' ? (
<TrendingUp className="w-5 h-5 text-yellow-600 mt-0.5 flex-shrink-0" />
) : (
<AlertTriangle className="w-5 h-5 text-gray-600 mt-0.5 flex-shrink-0" />
)}
<div>
<p className={`text-sm font-medium ${
analyticsData.systemLoadStatus === 'critical'
? 'text-red-800'
: analyticsData.systemLoadStatus === 'warning'
? 'text-orange-800'
: analyticsData.systemLoadStatus === 'monitor'
? 'text-blue-800'
: analyticsData.systemLoadStatus === 'low'
? 'text-yellow-800'
: 'text-gray-800'
}`}>
</p>
<p className={`text-sm mt-1 ${
analyticsData.systemLoadStatus === 'critical'
? 'text-red-700'
: analyticsData.systemLoadStatus === 'warning'
? 'text-orange-700'
: analyticsData.systemLoadStatus === 'monitor'
? 'text-blue-700'
: analyticsData.systemLoadStatus === 'low'
? 'text-yellow-700'
: 'text-gray-700'
}`}>
{analyticsData.systemLoadAdvice || '正在分析系統負載狀態...'}
</p>
</div>
</div>
</div>
</CardContent>
</Card>
{/* 應用類別分布 */}
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<ResponsiveContainer width="100%" height={300}>
<PieChart>
<Pie
data={analyticsData.categoryData}
cx="50%"
cy="50%"
outerRadius={90}
innerRadius={40}
fill="#8884d8"
dataKey="value"
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
labelLine={false}
>
{analyticsData.categoryData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} stroke="#ffffff" strokeWidth={2} />
))}
</Pie>
<Tooltip
formatter={(value, name, props) => {
const data = props.payload
return [
[`${value}%`, "占比"],
[`${data.users?.toLocaleString()}`, "用戶數"],
[`${data.apps}`, "應用數量"],
]
}}
labelFormatter={(label) => label}
contentStyle={{
backgroundColor: "white",
border: "1px solid #e5e7eb",
borderRadius: "8px",
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
fontSize: "14px",
}}
/>
</PieChart>
</ResponsiveContainer>
{/* 添加圖例說明 */}
<div className="mt-4 grid grid-cols-2 gap-2 text-sm">
{analyticsData.categoryData.map((category, index) => (
<div key={index} className="flex items-center gap-2">
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: category.color }} />
<span className="text-gray-700">{category.name}</span>
<span className="font-medium text-gray-900">{category.value}%</span>
</div>
))}
</div>
</CardContent>
</Card>
</div>
{/* 24小時使用模式 - 優化版 */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Clock className="w-5 h-5" />
24使
</CardTitle>
<p className="text-sm text-muted-foreground"></p>
</CardHeader>
<CardContent>
<div className="mb-4 flex flex-wrap gap-2">
<Badge variant="outline" className="bg-red-50 text-red-700 border-red-200">
<div className="w-3 h-3 bg-red-500 rounded mr-2"></div>
(80%+)
</Badge>
<Badge variant="outline" className="bg-blue-50 text-blue-700 border-blue-200">
<div className="w-3 h-3 bg-blue-500 rounded mr-2"></div>
使
</Badge>
<Badge variant="outline" className="bg-gray-50 text-gray-700 border-gray-200">
<div className="w-3 h-3 bg-gray-500 rounded mr-2"></div>
</Badge>
<Badge variant="outline" className="bg-gray-50 text-gray-600 border-gray-300">
<div className="w-3 h-3 bg-gray-400 rounded mr-2"></div>
</Badge>
</div>
<ResponsiveContainer width="100%" height={400}>
<BarChart data={hourlyData} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
<CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" />
<XAxis dataKey="hour" tick={{ fontSize: 12 }} tickFormatter={(value) => `${value}:00`} />
<YAxis tick={{ fontSize: 12 }} />
<Tooltip
formatter={(value, name, props) => {
const data = props.payload
const getIntensityText = (intensity: string) => {
switch (intensity) {
case "peak":
return "高峰期"
case "high":
return "高使用期"
case "normal":
return "正常期"
case "low":
return "低峰期"
default:
return "未知"
}
}
return [
[`${value}`, "同時在線用戶"],
[`${getIntensityText(data.intensity)}`, "時段分類"],
[`${data.cpuUsage}%`, "CPU使用率"],
[`${data.memoryUsage}%`, "記憶體使用率"],
[`${data.period}`, "時段特性"],
]
}}
labelFormatter={(label) => `${label}:00 時段`}
contentStyle={{
backgroundColor: "white",
border: "1px solid #e5e7eb",
borderRadius: "8px",
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
fontSize: "14px",
minWidth: "220px",
}}
/>
<Bar dataKey="users" radius={[4, 4, 0, 0]} fill={(entry: any) => getBarColor(entry.intensity)}>
{hourlyData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={getBarColor(entry.intensity)} />
))}
</Bar>
</BarChart>
</ResponsiveContainer>
<div className="mt-4 p-3 bg-blue-50 rounded-lg">
<p className="text-sm text-blue-800 flex items-center gap-2">
<TrendingUp className="w-4 h-4" />
<strong></strong>
{analyticsData.hourlyAnalysis || '正在分析24小時使用模式...'}
{analyticsData.hourlyAdvice && (
<span className="block mt-1 ml-6">
{analyticsData.hourlyAdvice}
</span>
)}
</p>
</div>
</CardContent>
</Card>
{/* 熱門應用排行 */}
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
{analyticsData.topApps.map((app, index) => (
<div key={index} className="flex items-center justify-between p-3 border rounded-lg">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center font-bold text-blue-600">
{index + 1}
</div>
<div>
<h3 className="font-medium">{app.name}</h3>
<p className="text-sm text-muted-foreground">{app.category}</p>
</div>
</div>
<div className="text-right">
<div className="flex items-center gap-2">
<Eye className="w-4 h-4 text-muted-foreground" />
<span className="font-medium">{app.views}</span>
</div>
<div className="flex items-center gap-1">
<Star className="w-4 h-4 text-yellow-400 fill-current" />
<span className="text-sm">{app.rating}</span>
</div>
</div>
</div>
))}
</div>
</CardContent>
</Card>
{/* 用戶回饋摘要 */}
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="text-center p-4 border rounded-lg">
<div className="text-2xl font-bold text-green-600">
{isLoading ? (
<Loader2 className="w-6 h-6 animate-spin mx-auto" />
) : (
`${analyticsData.satisfactionRate}%`
)}
</div>
<p className="text-sm text-muted-foreground">滿</p>
</div>
<div className="text-center p-4 border rounded-lg">
<div className="text-2xl font-bold text-blue-600">
{isLoading ? (
<Loader2 className="w-6 h-6 animate-spin mx-auto" />
) : (
analyticsData.userAvgRating || analyticsData.avgRating
)}
</div>
<p className="text-sm text-muted-foreground"></p>
{analyticsData.totalRatings > 0 && (
<p className="text-xs text-gray-500 mt-1">
{analyticsData.totalRatings}
</p>
)}
</div>
<div className="text-center p-4 border rounded-lg">
<div className="text-2xl font-bold text-purple-600">
{isLoading ? (
<Loader2 className="w-6 h-6 animate-spin mx-auto" />
) : (
analyticsData.weeklyFeedback
)}
</div>
<p className="text-sm text-muted-foreground"></p>
</div>
</div>
</CardContent>
</Card>
{/* 歷史數據查看模態框 */}
{showHistoryModal && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg p-6 w-full max-w-4xl max-h-[80vh] overflow-y-auto">
<div className="flex items-center justify-between mb-4">
<h2 className="text-xl font-bold"></h2>
<Button variant="ghost" size="sm" onClick={() => setShowHistoryModal(false)}>
</Button>
</div>
{/* 日期範圍選擇 */}
<div className="mb-6">
<div className="flex gap-2 mb-4">
{["近7天", "近30天", "近3個月", "近6個月"].map((range) => (
<Button
key={range}
variant={selectedDateRange === range ? "default" : "outline"}
size="sm"
onClick={() => setSelectedDateRange(range)}
>
{range}
</Button>
))}
</div>
</div>
{/* 歷史數據圖表 */}
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle>使 - {selectedDateRange}</CardTitle>
</CardHeader>
<CardContent>
<ResponsiveContainer width="100%" height={400}>
<ComposedChart data={getHistoricalData(selectedDateRange)}>
<defs>
<linearGradient id="colorUsersHistory" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#3b82f6" stopOpacity={0.3} />
<stop offset="95%" stopColor="#3b82f6" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" />
<XAxis dataKey="date" tick={{ fontSize: 12 }} />
<YAxis yAxisId="users" orientation="left" tick={{ fontSize: 12 }} />
<YAxis yAxisId="cpu" orientation="right" tick={{ fontSize: 12 }} />
<Tooltip
formatter={(value, name, props) => {
if (name === "users") {
return [`${value}`, "活躍用戶"]
}
if (name === "cpuPeak") {
return [`${value}%`, "CPU峰值"]
}
return [value, name]
}}
labelFormatter={(label, payload) => {
if (payload && payload.length > 0) {
const data = payload[0].payload
return data.fullDate || label
}
return label
}}
contentStyle={{
backgroundColor: "white",
border: "1px solid #e5e7eb",
borderRadius: "8px",
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
fontSize: "14px",
}}
/>
<Bar
yAxisId="users"
dataKey="users"
fill="url(#colorUsersHistory)"
radius={[2, 2, 0, 0]}
opacity={0.7}
/>
<Line
yAxisId="cpu"
type="monotone"
dataKey="cpuPeak"
stroke="#ef4444"
strokeWidth={2}
dot={{ fill: "#ef4444", strokeWidth: 1, r: 3 }}
/>
</ComposedChart>
</ResponsiveContainer>
</CardContent>
</Card>
{/* 歷史數據統計摘要 */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<Card>
<CardContent className="p-4">
<div className="text-center">
<div className="text-2xl font-bold text-blue-600">
{getHistoricalStats(selectedDateRange).avgUsers}
</div>
<p className="text-sm text-muted-foreground"></p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="p-4">
<div className="text-center">
<div className="text-2xl font-bold text-green-600">
{getHistoricalStats(selectedDateRange).maxUsers}
</div>
<p className="text-sm text-muted-foreground"></p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="p-4">
<div className="text-center">
<div className="text-2xl font-bold text-orange-600">
{getHistoricalStats(selectedDateRange).avgCpu}%
</div>
<p className="text-sm text-muted-foreground">CPU使用率</p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="p-4">
<div className="text-center">
<div className="text-2xl font-bold text-red-600">
{getHistoricalStats(selectedDateRange).maxCpu}%
</div>
<p className="text-sm text-muted-foreground">CPU使用率</p>
</div>
</CardContent>
</Card>
</div>
</div>
</div>
</div>
)}
</div>
)
}