From 6ce0b250cbb5951fc0408b834f097a9937aeedc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B3=E4=BD=A9=E5=BA=AD?= Date: Tue, 16 Sep 2025 17:19:20 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AB=B6=E8=B3=BD=E8=B3=87=E8=A8=8A=E5=AE=8C?= =?UTF-8?q?=E6=95=B4=E7=89=88=20(=20=E5=8F=AA=E5=89=A9=E8=A9=95=E5=88=86?= =?UTF-8?q?=E3=80=81=E7=AB=B6=E8=B3=BD=E5=BE=97=E7=8D=8E=20)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/competitions/[id]/teams/route.ts | 4 +- components/app-detail-dialog.tsx | 55 +++++++++++++++---- components/competition/team-detail-dialog.tsx | 18 ++++-- lib/services/database-service.ts | 12 ++-- 4 files changed, 67 insertions(+), 22 deletions(-) diff --git a/app/api/competitions/[id]/teams/route.ts b/app/api/competitions/[id]/teams/route.ts index a0d608c..db24e49 100644 --- a/app/api/competitions/[id]/teams/route.ts +++ b/app/api/competitions/[id]/teams/route.ts @@ -77,8 +77,8 @@ export async function GET(request: NextRequest, { params }: { params: { id: stri likes_count: app.likes_count || 0, views_count: app.views_count || 0, rating: Math.round(avgRating * 10) / 10, // 四捨五入到小數點後一位 - creator_name: "未知作者", - creator_department: "未知部門", + creator_name: app.creator_name || "未知作者", + creator_department: app.creator_department || "未知部門", team_name: team.name, created_at: app.created_at }; diff --git a/components/app-detail-dialog.tsx b/components/app-detail-dialog.tsx index edf27b3..5f96542 100644 --- a/components/app-detail-dialog.tsx +++ b/components/app-detail-dialog.tsx @@ -39,6 +39,11 @@ import { Trophy, Award, Medal, + Bot, + Code, + Database, + Palette, + Volume2, } from "lucide-react" import { FavoriteButton } from "./favorite-button" import { ReviewSystem } from "./reviews/review-system" @@ -48,15 +53,21 @@ interface AppDetailDialogProps { open: boolean onOpenChange: (open: boolean) => void app: { - id: number + id: string name: string type: string department: string description: string icon: any + iconColor?: string creator: string featured: boolean judgeScore: number + likesCount?: number + viewsCount?: number + rating?: number + reviewsCount?: number + createdAt?: string } } @@ -112,11 +123,17 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp // 圖標映射函數 const getIconComponent = (iconName: string) => { const iconMap: { [key: string]: any } = { - 'Bot': Brain, - 'ImageIcon': ImageIcon, + 'Brain': Brain, + 'Bot': Bot, + 'Code': Code, + 'Database': Database, + 'Palette': Palette, + 'Volume2': Volume2, + 'Search': Search, + 'BarChart3': BarChart3, 'Mic': Mic, + 'ImageIcon': ImageIcon, 'MessageSquare': MessageSquare, - 'Settings': Settings, 'Zap': Zap, 'TrendingUp': TrendingUp, 'Star': Star, @@ -128,21 +145,23 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp 'Target': Target, 'Users': Users, 'Lightbulb': Lightbulb, - 'Search': Search, 'Plus': Plus, 'X': X, 'ChevronLeft': ChevronLeft, 'ChevronRight': ChevronRight, - 'ArrowLeft': ArrowLeft + 'ArrowLeft': ArrowLeft, + 'Settings': Settings } return iconMap[iconName] || Brain // 預設使用 Brain 圖標 } const IconComponent = getIconComponent(app.icon || 'Bot') - const likes = (app as any).likesCount || 0 - const views = (app as any).viewsCount || 0 - const rating = (app as any).rating || 0 - const reviewsCount = (app as any).reviewsCount || 0 + // 優先使用從 API 載入的實際數據,如果沒有則使用 app 對象的數據 + const likes = appStats.basic?.likes || (app as any).likesCount || 0 + const views = appStats.basic?.views || (app as any).viewsCount || 0 + const rating = appStats.basic?.rating || (app as any).rating || 0 + const reviewsCount = appStats.basic?.reviewCount || (app as any).reviewsCount || 0 + const favorites = appStats.basic?.favorites || 0 // 使用從 API 載入的實際數據 const usageStats = appStats.usage @@ -169,13 +188,27 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp if (department) params.append('department', department) const url = `/api/apps/${app.id}/stats${params.toString() ? `?${params.toString()}` : ''}` + console.log('🔍 調用統計 API:', url) + console.log('🔍 應用 ID:', app.id) + const response = await fetch(url) + console.log('🔍 API 響應狀態:', response.status, response.statusText) + + if (!response.ok) { + console.error('❌ API 響應錯誤:', response.status, response.statusText) + return + } + const data = await response.json() + console.log('🔍 API 響應數據:', data) if (data.success) { + console.log('📊 應用統計數據加載成功:', data.data) setAppStats(data.data) setCurrentRating(data.data.basic.rating) setReviewCount(data.data.basic.reviewCount) + } else { + console.error('❌ 應用統計數據加載失敗:', data) } } catch (error) { console.error('載入應用統計數據錯誤:', error) @@ -489,7 +522,7 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp
- {isLoadingStats ? '...' : (appStats.basic as any).favorites || 0} + {isLoadingStats ? '...' : favorites}

用戶收藏數量

diff --git a/components/competition/team-detail-dialog.tsx b/components/competition/team-detail-dialog.tsx index edec98a..abcb3f2 100644 --- a/components/competition/team-detail-dialog.tsx +++ b/components/competition/team-detail-dialog.tsx @@ -70,16 +70,19 @@ const getAppDetails = (appId: string, team: any) => { type: appDetail.type || "未知類型", description: appDetail.description || "無描述", icon: getIconComponent(appDetail.icon) || Brain, + iconColor: appDetail.icon_color || "from-blue-500 to-purple-500", fullDescription: appDetail.description || "無描述", features: [], author: appDetail.creator_name || "未知作者", + department: appDetail.creator_department || "未知部門", category: appDetail.category || "未分類", tags: [], demoUrl: "", sourceUrl: "", likes: appDetail.likes_count || 0, views: appDetail.views_count || 0, - rating: Number(appDetail.rating) || 0 + rating: Number(appDetail.rating) || 0, + createdAt: appDetail.created_at }; } @@ -126,17 +129,22 @@ export function TeamDetailDialog({ open, onOpenChange, team }: TeamDetailDialogP const appDetails = getAppDetails(appId, team) // Create app object that matches AppDetailDialog interface const app = { - id: Number.parseInt(appId), + id: appId, // 保持為字符串,API 期望字符串 ID name: appDetails.name, type: appDetails.type, - department: team.department, // Use team's department + department: appDetails.department, // Use app's creator department description: appDetails.description, icon: appDetails.icon, + iconColor: appDetails.iconColor, // Pass icon color creator: appDetails.author, featured: false, judgeScore: appDetails.rating || 0, - likes: appDetails.likes || 0, - views: appDetails.views || 0, + // 使用 AppDetailDialog 期望的屬性名 + likesCount: appDetails.likes || 0, + viewsCount: appDetails.views || 0, + rating: appDetails.rating || 0, + reviewsCount: 0, // 可以從 API 獲取,暫時設為 0 + createdAt: appDetails.createdAt, // Pass creation date } setSelectedApp(app) setAppDetailOpen(true) diff --git a/lib/services/database-service.ts b/lib/services/database-service.ts index 6964e3e..d828c28 100644 --- a/lib/services/database-service.ts +++ b/lib/services/database-service.ts @@ -1151,10 +1151,14 @@ export class TeamService { static async getTeamApps(teamId: string): Promise { console.log('🔍 TeamService.getTeamApps 被調用, teamId:', teamId); const sql = ` - SELECT id, name, description, category, type, icon, icon_color, app_url, likes_count, views_count, rating - FROM apps - WHERE team_id = ? AND is_active = TRUE - ORDER BY created_at DESC + SELECT + a.id, a.name, a.description, a.category, a.type, a.icon, a.icon_color, + a.app_url, a.likes_count, a.views_count, a.rating, a.created_at, + u.name as creator_name, u.department as creator_department + FROM apps a + LEFT JOIN users u ON a.creator_id = u.id + WHERE a.team_id = ? AND a.is_active = TRUE + ORDER BY a.created_at DESC `; console.log('📝 getTeamApps SQL:', sql); console.log('📝 getTeamApps 參數:', [teamId]);