新增競賽前台呈現、刪除競賽、修改競賽狀態

This commit is contained in:
2025-09-16 14:57:40 +08:00
parent 1f2fb14bd0
commit b4386dc481
21 changed files with 1714 additions and 127 deletions

View File

@@ -20,6 +20,13 @@ import {
Brain,
Zap,
ExternalLink,
Bot,
Code,
Database,
Palette,
Volume2,
Search,
BarChart3,
} from "lucide-react"
import { useAuth } from "@/contexts/auth-context"
import { LikeButton } from "@/components/like-button"
@@ -31,22 +38,68 @@ interface TeamDetailDialogProps {
team: any
}
// App data for team apps - empty for production
const getAppDetails = (appId: string) => {
// 圖標映射函數
const getIconComponent = (iconName: string) => {
const iconMap: { [key: string]: any } = {
'Brain': Brain,
'Bot': Bot,
'Code': Code,
'Database': Database,
'Palette': Palette,
'Volume2': Volume2,
'Search': Search,
'BarChart3': BarChart3,
'Mic': Mic,
'ImageIcon': ImageIcon,
'MessageSquare': MessageSquare,
'Zap': Zap,
'TrendingUp': TrendingUp,
};
return iconMap[iconName] || Brain;
}
// App data for team apps - get from team data
const getAppDetails = (appId: string, team: any) => {
const appDetail = team.appsDetails?.find((app: any) => app.id === appId);
if (appDetail) {
return {
id: appDetail.id,
name: appDetail.name || "未命名應用",
type: appDetail.type || "未知類型",
description: appDetail.description || "無描述",
icon: getIconComponent(appDetail.icon) || Brain,
fullDescription: appDetail.description || "無描述",
features: [],
author: appDetail.creator_name || "未知作者",
category: appDetail.category || "未分類",
tags: [],
demoUrl: "",
sourceUrl: "",
likes: appDetail.likes_count || 0,
views: appDetail.views_count || 0,
rating: Number(appDetail.rating) || 0
};
}
return {
id: appId,
name: "",
type: "",
description: "",
icon: null,
fullDescription: "",
name: "未命名應用",
type: "未知類型",
description: "無描述",
icon: Brain,
fullDescription: "無描述",
features: [],
author: "",
category: "",
author: "未知作者",
category: "未分類",
tags: [],
demoUrl: "",
sourceUrl: "",
}
likes: 0,
views: 0,
rating: 0
};
}
const getTypeColor = (type: string) => {
@@ -67,10 +120,10 @@ export function TeamDetailDialog({ open, onOpenChange, team }: TeamDetailDialogP
if (!team) return null
const leader = team.members.find((m: any) => m.id === team.leader)
const leader = team.members.find((m: any) => m.user_id === team.leader)
const handleAppClick = (appId: string) => {
const appDetails = getAppDetails(appId)
const appDetails = getAppDetails(appId, team)
// Create app object that matches AppDetailDialog interface
const app = {
id: Number.parseInt(appId),
@@ -81,14 +134,17 @@ export function TeamDetailDialog({ open, onOpenChange, team }: TeamDetailDialogP
icon: appDetails.icon,
creator: appDetails.author,
featured: false,
judgeScore: 0,
judgeScore: appDetails.rating || 0,
likes: appDetails.likes || 0,
views: appDetails.views || 0,
}
setSelectedApp(app)
setAppDetailOpen(true)
}
const totalLikes = team.apps.reduce((sum: number, appId: string) => sum + getLikeCount(appId), 0)
const totalViews = team.apps.reduce((sum: number, appId: string) => sum + getViewCount(appId), 0)
// 使用從數據庫獲取的真實數據
const totalLikes = team.totalLikes || 0
const totalViews = team.totalViews || 0
return (
<>
@@ -179,13 +235,13 @@ export function TeamDetailDialog({ open, onOpenChange, team }: TeamDetailDialogP
</div>
<div>
<label className="text-sm font-medium text-gray-700"></label>
<p className="text-gray-900">{leader?.name}</p>
<p className="text-gray-900">{leader?.name || '未指定'}</p>
</div>
<div>
<label className="text-sm font-medium text-gray-700"></label>
<div className="flex items-center space-x-2">
<Mail className="w-4 h-4 text-gray-500" />
<p className="text-gray-900">{team.contactEmail}</p>
<p className="text-gray-900">{team.contact_email || '未提供'}</p>
</div>
</div>
</div>
@@ -261,11 +317,12 @@ export function TeamDetailDialog({ open, onOpenChange, team }: TeamDetailDialogP
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{team.apps.map((appId: string) => {
const app = getAppDetails(appId)
const IconComponent = app.icon
const likes = getLikeCount(appId)
const views = getViewCount(appId)
const rating = getAppRating(appId)
const app = getAppDetails(appId, team)
// 如果沒有圖標,使用默認的 Brain 圖標
const IconComponent = app.icon || Brain
const likes = app.likes || 0
const views = app.views || 0
const rating = app.rating || 0
return (
<Card
@@ -299,11 +356,16 @@ export function TeamDetailDialog({ open, onOpenChange, team }: TeamDetailDialogP
</div>
<div className="flex items-center space-x-1">
<Star className="w-3 h-3 text-yellow-500" />
<span>{rating.toFixed(1)}</span>
<span>{Number(rating).toFixed(1)}</span>
</div>
</div>
<div onClick={(e) => e.stopPropagation()}>
<LikeButton appId={appId} size="sm" />
<LikeButton
appId={appId}
size="sm"
likeCount={likes}
showCount={true}
/>
</div>
</div>
</CardContent>