修改管理者數據分析數據
This commit is contained in:
@@ -231,11 +231,6 @@ export function AdminLayout({ children, currentPage, onPageChange }: AdminLayout
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-2">存取被拒</h2>
|
||||
<p className="text-gray-600 mb-4">您沒有管理員權限訪問此頁面</p>
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<div className="text-xs text-gray-500 mb-4">
|
||||
調試信息: 用戶={user ? '已登入' : '未登入'}, 角色={user?.role || '無'}
|
||||
</div>
|
||||
)}
|
||||
<div className="space-x-3">
|
||||
<Button onClick={() => {
|
||||
if (isClient) {
|
||||
|
@@ -32,40 +32,51 @@ export function AnalyticsDashboard() {
|
||||
totalApps: 0,
|
||||
newThisWeek: 0,
|
||||
userGrowth: 0,
|
||||
userGrowthText: "較上月",
|
||||
dailyUsageData: [],
|
||||
categoryData: [],
|
||||
topApps: [],
|
||||
hourlyData: [],
|
||||
satisfactionRate: 0,
|
||||
weeklyFeedback: 0
|
||||
weeklyFeedback: 0,
|
||||
userAvgRating: 0,
|
||||
totalRatings: 0,
|
||||
systemLoadStatus: "normal",
|
||||
systemLoadAdvice: "",
|
||||
maxCpuPeak: 0,
|
||||
maxDailyUsers: 0,
|
||||
avgDailyUsers: 0,
|
||||
totalWeeklySessions: 0,
|
||||
hourlyAnalysis: "",
|
||||
hourlyAdvice: ""
|
||||
})
|
||||
|
||||
// 24小時使用數據 - 優化版本
|
||||
const hourlyData = [
|
||||
{ hour: "00", users: 39, period: "深夜", intensity: "low", cpuUsage: 25, memoryUsage: 45 },
|
||||
{ hour: "01", users: 62, period: "深夜", intensity: "normal", cpuUsage: 22, memoryUsage: 43 },
|
||||
{ hour: "02", users: 24, period: "深夜", intensity: "low", cpuUsage: 20, memoryUsage: 41 },
|
||||
{ hour: "03", users: 40, period: "深夜", intensity: "low", cpuUsage: 18, memoryUsage: 40 },
|
||||
{ hour: "04", users: 40, period: "深夜", intensity: "low", cpuUsage: 17, memoryUsage: 39 },
|
||||
{ hour: "05", users: 55, period: "清晨", intensity: "normal", cpuUsage: 19, memoryUsage: 41 },
|
||||
{ hour: "06", users: 26, period: "清晨", intensity: "low", cpuUsage: 28, memoryUsage: 48 },
|
||||
{ hour: "07", users: 67, period: "清晨", intensity: "normal", cpuUsage: 35, memoryUsage: 52 },
|
||||
{ hour: "08", users: 26, period: "工作時間", intensity: "normal", cpuUsage: 42, memoryUsage: 58 },
|
||||
{ hour: "09", users: 89, period: "工作時間", intensity: "high", cpuUsage: 58, memoryUsage: 68 },
|
||||
{ hour: "10", users: 88, period: "工作時間", intensity: "high", cpuUsage: 65, memoryUsage: 72 },
|
||||
{ hour: "11", users: 129, period: "工作時間", intensity: "peak", cpuUsage: 72, memoryUsage: 76 },
|
||||
{ hour: "12", users: 106, period: "工作時間", intensity: "peak", cpuUsage: 62, memoryUsage: 70 },
|
||||
{ hour: "13", users: 105, period: "工作時間", intensity: "peak", cpuUsage: 68, memoryUsage: 74 },
|
||||
{ hour: "14", users: 81, period: "工作時間", intensity: "high", cpuUsage: 78, memoryUsage: 82 },
|
||||
{ hour: "15", users: 119, period: "工作時間", intensity: "peak", cpuUsage: 74, memoryUsage: 79 },
|
||||
{ hour: "16", users: 126, period: "工作時間", intensity: "peak", cpuUsage: 67, memoryUsage: 73 },
|
||||
{ hour: "17", users: 112, period: "工作時間", intensity: "peak", cpuUsage: 59, memoryUsage: 67 },
|
||||
{ hour: "18", users: 22, period: "晚間", intensity: "low", cpuUsage: 45, memoryUsage: 58 },
|
||||
{ hour: "19", users: 60, period: "晚間", intensity: "normal", cpuUsage: 38, memoryUsage: 53 },
|
||||
{ hour: "20", users: 32, period: "晚間", intensity: "low", cpuUsage: 33, memoryUsage: 50 },
|
||||
{ hour: "21", users: 22, period: "晚間", intensity: "low", cpuUsage: 29, memoryUsage: 47 },
|
||||
{ hour: "22", users: 36, period: "晚間", intensity: "low", cpuUsage: 26, memoryUsage: 46 },
|
||||
{ hour: "23", users: 66, period: "晚間", intensity: "normal", cpuUsage: 24, memoryUsage: 44 },
|
||||
]
|
||||
// 載入分析數據
|
||||
const loadAnalyticsData = async () => {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
const response = await fetch('/api/admin/analytics')
|
||||
const data = await response.json()
|
||||
|
||||
if (data.success) {
|
||||
setAnalyticsData(data.data)
|
||||
} 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) => {
|
||||
@@ -126,38 +137,28 @@ export function AnalyticsDashboard() {
|
||||
{ name: "文本分析工具", views: 692, rating: 4.4, category: "AI工具" },
|
||||
]
|
||||
|
||||
// 獲取歷史數據
|
||||
// 獲取歷史數據 - 使用真實數據
|
||||
const getHistoricalData = (range: string) => {
|
||||
const baseData = [
|
||||
{ date: "12/1", users: 180, cpuPeak: 55, fullDate: "2024/12/1" },
|
||||
{ date: "12/8", users: 210, cpuPeak: 62, fullDate: "2024/12/8" },
|
||||
{ date: "12/15", users: 245, cpuPeak: 68, fullDate: "2024/12/15" },
|
||||
{ date: "12/22", users: 280, cpuPeak: 74, fullDate: "2024/12/22" },
|
||||
{ date: "12/29", users: 320, cpuPeak: 78, fullDate: "2024/12/29" },
|
||||
{ date: "1/5", users: 298, cpuPeak: 73, fullDate: "2025/1/5" },
|
||||
{ date: "1/12", users: 334, cpuPeak: 79, fullDate: "2025/1/12" },
|
||||
{ date: "1/19", users: 356, cpuPeak: 82, fullDate: "2025/1/19" },
|
||||
]
|
||||
|
||||
switch (range) {
|
||||
case "近7天":
|
||||
return dailyUsageData
|
||||
case "近30天":
|
||||
return baseData.slice(-4)
|
||||
case "近3個月":
|
||||
return baseData.slice(-6)
|
||||
case "近6個月":
|
||||
return baseData
|
||||
default:
|
||||
return dailyUsageData
|
||||
}
|
||||
// 所有時間範圍都使用真實的 dailyUsageData
|
||||
// 因為我們已經在API中獲取了近7天的真實數據
|
||||
return analyticsData.dailyUsageData || []
|
||||
}
|
||||
|
||||
// 獲取歷史統計數據
|
||||
// 獲取歷史統計數據 - 使用真實數據
|
||||
const getHistoricalStats = (range: string) => {
|
||||
const data = getHistoricalData(range)
|
||||
const users = data.map((d) => d.users)
|
||||
const cpus = data.map((d) => d.cpuPeak)
|
||||
|
||||
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),
|
||||
@@ -171,10 +172,21 @@ export function AnalyticsDashboard() {
|
||||
<div className="p-6 space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-3xl font-bold">數據分析</h1>
|
||||
<Badge variant="outline" className="text-sm">
|
||||
<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" />
|
||||
)}
|
||||
即時更新
|
||||
</Badge>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 關鍵指標卡片 */}
|
||||
@@ -185,9 +197,21 @@ export function AnalyticsDashboard() {
|
||||
<Users className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">2,847</div>
|
||||
<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">
|
||||
<span className="text-green-600">+12.5%</span> 較上月
|
||||
{isLoading ? (
|
||||
"載入中..."
|
||||
) : (
|
||||
<>
|
||||
<span className="text-green-600">+{analyticsData.userGrowth}%</span> {analyticsData.userGrowthText}
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -198,9 +222,21 @@ export function AnalyticsDashboard() {
|
||||
<Eye className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">356</div>
|
||||
<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">
|
||||
<span className="text-green-600">+8.2%</span> 較昨日
|
||||
{isLoading ? (
|
||||
"載入中..."
|
||||
) : (
|
||||
<>
|
||||
<span className="text-green-600">+{analyticsData.todayActiveGrowth}%</span> 較昨日
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -211,9 +247,21 @@ export function AnalyticsDashboard() {
|
||||
<Star className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">4.6</div>
|
||||
<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">
|
||||
<span className="text-green-600">+0.3</span> 較上週
|
||||
{isLoading ? (
|
||||
"載入中..."
|
||||
) : (
|
||||
<>
|
||||
<span className="text-green-600">+{analyticsData.ratingGrowth}</span> 較上週
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -224,9 +272,21 @@ export function AnalyticsDashboard() {
|
||||
<TrendingUp className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">127</div>
|
||||
<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">
|
||||
<span className="text-green-600">+5</span> 本週新增
|
||||
{isLoading ? (
|
||||
"載入中..."
|
||||
) : (
|
||||
<>
|
||||
<span className="text-green-600">+{analyticsData.newThisWeek}</span> 本週新增
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -251,7 +311,7 @@ export function AnalyticsDashboard() {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ResponsiveContainer width="100%" height={320}>
|
||||
<ComposedChart data={dailyUsageData}>
|
||||
<ComposedChart data={analyticsData.dailyUsageData}>
|
||||
<defs>
|
||||
<linearGradient id="colorUsers" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#3b82f6" stopOpacity={0.3} />
|
||||
@@ -319,14 +379,56 @@ export function AnalyticsDashboard() {
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
|
||||
{/* 系統建議 */}
|
||||
<div className="mt-4 p-3 bg-orange-50 rounded-lg border border-orange-200">
|
||||
{/* 系統建議 - 動態狀態 */}
|
||||
<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 text-orange-800">系統負載建議</p>
|
||||
<p className="text-sm text-orange-700 mt-1">
|
||||
近7天CPU峰值達82%,當用戶數超過350時系統負載顯著增加。建議考慮硬體升級或負載均衡優化。
|
||||
<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>
|
||||
@@ -343,7 +445,7 @@ export function AnalyticsDashboard() {
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={categoryData}
|
||||
data={analyticsData.categoryData}
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
outerRadius={90}
|
||||
@@ -353,7 +455,7 @@ export function AnalyticsDashboard() {
|
||||
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
|
||||
labelLine={false}
|
||||
>
|
||||
{categoryData.map((entry, index) => (
|
||||
{analyticsData.categoryData.map((entry, index) => (
|
||||
<Cell key={`cell-${index}`} fill={entry.color} stroke="#ffffff" strokeWidth={2} />
|
||||
))}
|
||||
</Pie>
|
||||
@@ -380,7 +482,7 @@ export function AnalyticsDashboard() {
|
||||
|
||||
{/* 添加圖例說明 */}
|
||||
<div className="mt-4 grid grid-cols-2 gap-2 text-sm">
|
||||
{categoryData.map((category, index) => (
|
||||
{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>
|
||||
@@ -473,7 +575,13 @@ export function AnalyticsDashboard() {
|
||||
<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>工作時間 09:00-17:00 為主要使用時段,建議在此時段確保系統穩定性
|
||||
<strong>尖峰時段分析:</strong>
|
||||
{analyticsData.hourlyAnalysis || '正在分析24小時使用模式...'}
|
||||
{analyticsData.hourlyAdvice && (
|
||||
<span className="block mt-1 ml-6">
|
||||
{analyticsData.hourlyAdvice}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -486,7 +594,7 @@ export function AnalyticsDashboard() {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
{topApps.map((app, index) => (
|
||||
{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">
|
||||
@@ -521,15 +629,38 @@ export function AnalyticsDashboard() {
|
||||
<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">92%</div>
|
||||
<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">4.6</div>
|
||||
<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">156</div>
|
||||
<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>
|
||||
|
Reference in New Issue
Block a user