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

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

@@ -1,6 +1,6 @@
"use client"
import { useState } from "react"
import { useState, useEffect } from "react"
import { useAuth } from "@/contexts/auth-context"
import { useCompetition } from "@/contexts/competition-context"
import {
@@ -55,10 +55,67 @@ export function PopularityRankings() {
const [individualCurrentPage, setIndividualCurrentPage] = useState(0)
const [teamCurrentPage, setTeamCurrentPage] = useState(0)
// 新增狀態
const [competitionApps, setCompetitionApps] = useState<any[]>([])
const [competitionTeams, setCompetitionTeams] = useState<any[]>([])
const [competitionJudges, setCompetitionJudges] = useState<any[]>([])
const [isLoading, setIsLoading] = useState(false)
const ITEMS_PER_PAGE = 3
// 載入當前競賽的數據
useEffect(() => {
if (currentCompetition) {
loadCompetitionData(currentCompetition.id)
} else {
// 如果沒有當前競賽,清空數據
setCompetitionApps([])
setCompetitionTeams([])
setCompetitionJudges([])
}
}, [currentCompetition])
const loadCompetitionData = async (competitionId: string) => {
setIsLoading(true)
try {
// 並行載入競賽的應用、團隊和評審數據
const [appsResponse, teamsResponse, judgesResponse] = await Promise.all([
fetch(`/api/competitions/${competitionId}/apps`), // 移除 competitionType 參數,載入所有應用
fetch(`/api/competitions/${competitionId}/teams`),
fetch(`/api/competitions/${competitionId}/judges`)
])
const [appsData, teamsData, judgesData] = await Promise.all([
appsResponse.json(),
teamsResponse.json(),
judgesResponse.json()
])
if (appsData.success) {
// 合併個人應用和團隊應用
const allApps = appsData.data.apps || []
console.log('📱 載入的應用數據:', allApps)
setCompetitionApps(allApps)
}
if (teamsData.success) {
const teams = teamsData.data.teams || []
console.log('👥 載入的團隊數據:', teams)
setCompetitionTeams(teams)
}
if (judgesData.success) {
const judges = judgesData.data.judges || []
console.log('👨‍⚖️ 載入的評審數據:', judges)
setCompetitionJudges(judges)
}
} catch (error) {
console.error('載入競賽數據失敗:', error)
} finally {
setIsLoading(false)
}
}
// Filter apps based on search criteria
const filteredApps = aiApps.filter((app) => {
const filteredApps = competitionApps.filter((app) => {
const matchesSearch =
app.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
app.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
@@ -72,9 +129,7 @@ export function PopularityRankings() {
// Sort apps by like count (popularity) and group by competition type
const sortedApps = filteredApps.sort((a, b) => {
const likesA = getLikeCount(a.id.toString())
const likesB = getLikeCount(b.id.toString())
return likesB - likesA
return (b.likes || 0) - (a.likes || 0)
})
// Group apps by competition type
@@ -319,23 +374,8 @@ export function PopularityRankings() {
return null
}
// Calculate team popularity score: total apps × highest like count
const teamsWithScores = teams
.map((team) => {
const appLikes = team.apps.map((appId: string) => getLikeCount(appId))
const maxLikes = Math.max(...appLikes, 0)
const totalApps = team.apps.length
const popularityScore = totalApps * maxLikes
return {
...team,
popularityScore,
maxLikes,
totalApps,
totalViews: team.apps.reduce((sum: number, appId: string) => sum + getViewCount(appId), 0),
}
})
.sort((a, b) => b.popularityScore - a.popularityScore)
// 團隊已經從 API 獲取了人氣分數,直接使用
const teamsWithScores = teams.sort((a, b) => b.popularityScore - a.popularityScore)
const currentPage = teamCurrentPage
const setCurrentPage = setTeamCurrentPage
@@ -512,7 +552,7 @@ export function PopularityRankings() {
<div className="flex items-center space-x-2">
<ThumbsUp className="w-4 h-4 text-blue-500" />
<span className="text-sm font-medium">
{team.apps.reduce((sum: number, appId: string) => sum + getLikeCount(appId), 0)}
{team.totalLikes || 0}
</span>
</div>
<Button
@@ -593,7 +633,11 @@ export function PopularityRankings() {
</div>
<div className="flex items-center space-x-2">
<Users className="w-4 h-4 text-gray-500" />
<span className="text-sm">{filteredApps.length} </span>
<span className="text-sm">
{currentCompetition.type === 'team'
? `${competitionTeams.length} 個參賽團隊`
: `${filteredApps.length} 個參賽應用`}
</span>
</div>
<div className="flex items-center space-x-2">
<Badge
@@ -633,27 +677,39 @@ export function PopularityRankings() {
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{judges.map((judge) => (
<div key={judge.id} className="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg">
<Avatar>
<AvatarImage src={judge.avatar || "/placeholder.svg"} />
<AvatarFallback className="bg-purple-100 text-purple-700">{judge.name[0]}</AvatarFallback>
</Avatar>
<div className="flex-1">
<h4 className="font-medium">{judge.name}</h4>
<p className="text-sm text-gray-600">{judge.title}</p>
<div className="flex flex-wrap gap-1 mt-1">
{judge.expertise.slice(0, 2).map((skill) => (
<Badge key={skill} variant="secondary" className="text-xs">
{skill}
</Badge>
))}
{isLoading ? (
<div className="text-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-purple-600 mx-auto"></div>
<p className="mt-2 text-gray-600">...</p>
</div>
) : competitionJudges.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{competitionJudges.map((judge) => (
<div key={judge.id} className="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg">
<Avatar>
<AvatarImage src={judge.avatar || "/placeholder.svg"} />
<AvatarFallback className="bg-purple-100 text-purple-700">{judge.name[0]}</AvatarFallback>
</Avatar>
<div className="flex-1">
<h4 className="font-medium">{judge.name}</h4>
<p className="text-sm text-gray-600">{judge.title}</p>
<div className="flex flex-wrap gap-1 mt-1">
{judge.expertise.slice(0, 2).map((skill) => (
<Badge key={skill} variant="secondary" className="text-xs">
{skill}
</Badge>
))}
</div>
</div>
</div>
</div>
))}
</div>
))}
</div>
) : (
<div className="text-center py-8 text-gray-500">
<Crown className="w-8 h-8 mx-auto mb-2 opacity-50" />
<p></p>
</div>
)}
</CardContent>
</Card>
@@ -733,23 +789,30 @@ export function PopularityRankings() {
{/* Team Competition Section */}
{(selectedCompetitionType === "all" || selectedCompetitionType === "team") &&
currentCompetition?.type === "team" &&
renderTeamCompetitionSection(
mockTeams.filter(
(team) =>
team.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
team.members.some((member: any) => member.name.toLowerCase().includes(searchTerm.toLowerCase())) ||
selectedDepartment === "all" ||
team.department === selectedDepartment,
competitionTeams.filter(
(team) => {
const matchesSearch = searchTerm === "" ||
team.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
team.members?.some((member: any) => member.name.toLowerCase().includes(searchTerm.toLowerCase()));
const matchesDepartment = selectedDepartment === "all" || team.department === selectedDepartment;
return matchesSearch && matchesDepartment;
}
),
"團隊賽",
)}
{/* No Results */}
{filteredApps.length === 0 && (
{filteredApps.length === 0 && competitionTeams.length === 0 && (
<Card>
<CardContent className="text-center py-12">
<Heart className="w-16 h-16 text-gray-400 mx-auto mb-4" />
<h3 className="text-xl font-semibold text-gray-600 mb-2"></h3>
<h3 className="text-xl font-semibold text-gray-600 mb-2">
{currentCompetition?.type === 'team' ? '沒有找到符合條件的團隊' : '沒有找到符合條件的應用'}
</h3>
<p className="text-gray-500">調</p>
<Button
variant="outline"