"use client" import React, { useState, useEffect } from "react" import { useCompetition } from "@/contexts/competition-context" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Textarea } from "@/components/ui/textarea" import { Badge } from "@/components/ui/badge" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" import { Alert, AlertDescription } from "@/components/ui/alert" import { Separator } from "@/components/ui/separator" import { Progress } from "@/components/ui/progress" import { Checkbox } from "@/components/ui/checkbox" import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "@/components/ui/pagination" import { Trophy, Plus, Award, MoreHorizontal, Edit, Play, CheckCircle, AlertTriangle, Crown, UserPlus, Loader2, Star, StarOff, Users, Settings, ClipboardList, Link, UserCheck, UserX, Upload, Filter, User, Mail, Trash2, Eye, Search, X, } from "lucide-react" import type { CompetitionRule, CompetitionAwardType } from "@/types/competition" import { ScoringManagement } from "./scoring-management" // Competition data - empty for production const mockIndividualApps: any[] = [] // Teams data - empty for production const initialTeams: any[] = [] export function CompetitionManagement() { const { competitions, currentCompetition, setCurrentCompetition, addCompetition, updateCompetition, deleteCompetition, judges, addJudge, updateJudge, deleteJudge, awards, addAward, getAppDetailedScores, judgeScores, getAppJudgeScores, submitJudgeScore, } = useCompetition() // Teams state - managed locally for now const [teams, setTeams] = useState(initialTeams) // 資料庫整合狀態 const [dbCompetitions, setDbCompetitions] = useState([]) const [isLoadingDb, setIsLoadingDb] = useState(false) const [dbStats, setDbStats] = useState(null) // 評審資料庫整合狀態 const [dbJudges, setDbJudges] = useState([]) const [isLoadingJudges, setIsLoadingJudges] = useState(false) const [judgeStats, setJudgeStats] = useState(null) // 團隊資料庫整合狀態 const [dbTeams, setDbTeams] = useState([]) const [isLoadingTeams, setIsLoadingTeams] = useState(false) const [teamStats, setTeamStats] = useState(null) // 可用應用狀態 const [availableApps, setAvailableApps] = useState([]) const [isLoadingApps, setIsLoadingApps] = useState(false) // 可用用戶狀態 const [availableUsers, setAvailableUsers] = useState([]) const [isLoadingUsers, setIsLoadingUsers] = useState(false) const [showCreateCompetition, setShowCreateCompetition] = useState(false) const [showAddJudge, setShowAddJudge] = useState(false) const [showCreateAward, setShowCreateAward] = useState(false) const [showScoringManagement, setShowScoringManagement] = useState(false) const [showJudgeLinks, setShowJudgeLinks] = useState(false) const [showManualScoring, setShowManualScoring] = useState(false) const [showCreateTeam, setShowCreateTeam] = useState(false) const [showTeamDetail, setShowTeamDetail] = useState(false) const [showDeleteTeamConfirm, setShowDeleteTeamConfirm] = useState(false) const [selectedCompetition, setSelectedCompetition] = useState(null) const [selectedTeam, setSelectedTeam] = useState(null) const [teamToDelete, setTeamToDelete] = useState(null) const [isLoading, setIsLoading] = useState(false) const [success, setSuccess] = useState("") const [error, setError] = useState("") const [showCompetitionDetail, setShowCompetitionDetail] = useState(false) const [showDeleteCompetitionConfirm, setShowDeleteCompetitionConfirm] = useState(false) const [showChangeStatusDialog, setShowChangeStatusDialog] = useState(false) const [selectedCompetitionForAction, setSelectedCompetitionForAction] = useState(null) const [newStatus, setNewStatus] = useState("") // 奖项搜索和筛选状态 const [awardSearchQuery, setAwardSearchQuery] = useState("") const [awardYearFilter, setAwardYearFilter] = useState("all") // 資料庫 API 調用函數 const fetchCompetitions = async () => { setIsLoadingDb(true) try { const response = await fetch('/api/admin/competitions') const data = await response.json() if (data.success) { setDbCompetitions(data.data) } else { setError('獲取競賽列表失敗: ' + data.message) } } catch (error) { console.error('獲取競賽列表失敗:', error) setError('獲取競賽列表失敗') } finally { setIsLoadingDb(false) } } const fetchCompetitionStats = async () => { try { const response = await fetch('/api/admin/competitions/stats') const data = await response.json() if (data.success) { setDbStats(data.data) } } catch (error) { console.error('獲取競賽統計失敗:', error) } } const createCompetitionInDb = async (competitionData: any) => { try { const response = await fetch('/api/admin/competitions', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(competitionData) }) const data = await response.json() if (data.success) { setSuccess('競賽創建成功!') await fetchCompetitions() // 重新獲取列表 await fetchCompetitionStats() // 重新獲取統計 return data.data } else { setError('創建競賽失敗: ' + data.message) return null } } catch (error) { console.error('創建競賽失敗:', error) setError('創建競賽失敗') return null } } const updateCompetitionInDb = async (id: string, updates: any) => { try { const response = await fetch(`/api/admin/competitions/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(updates) }) const data = await response.json() if (data.success) { setSuccess('競賽更新成功!') await fetchCompetitions() // 重新獲取列表 await fetchCompetitionStats() // 重新獲取統計 return data.data } else { setError('更新競賽失敗: ' + data.message) return null } } catch (error) { console.error('更新競賽失敗:', error) setError('更新競賽失敗') return null } } const deleteCompetitionInDb = async (id: string) => { try { const response = await fetch(`/api/admin/competitions/${id}`, { method: 'DELETE' }) const data = await response.json() if (data.success) { setSuccess('競賽刪除成功!') await fetchCompetitions() // 重新獲取列表 await fetchCompetitionStats() // 重新獲取統計 return true } else { setError('刪除競賽失敗: ' + data.message) return false } } catch (error) { console.error('刪除競賽失敗:', error) setError('刪除競賽失敗') return false } } // 評審資料庫 API 調用函數 const fetchJudges = async () => { setIsLoadingJudges(true) try { // 添加時間戳避免緩存 const response = await fetch(`/api/admin/judges?t=${Date.now()}`, { cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }) const data = await response.json() console.log('🔍 fetchJudges 響應:', data) if (data.success) { console.log('📊 獲取到的評審數據:', data.data) setDbJudges(data.data) } else { setError('獲取評審列表失敗: ' + data.message) } } catch (error) { console.error('獲取評審列表失敗:', error) setError('獲取評審列表失敗') } finally { setIsLoadingJudges(false) } } const fetchJudgeStats = async () => { try { const response = await fetch('/api/admin/judges/stats') const data = await response.json() console.log('評審統計 API 響應:', data) if (data.success) { setJudgeStats(data.data) console.log('評審統計設置成功:', data.data) } else { console.error('評審統計 API 失敗:', data.message) } } catch (error) { console.error('獲取評審統計失敗:', error) } } // 團隊資料庫 API 調用函數 const fetchTeams = async () => { setIsLoadingTeams(true) try { // 添加時間戳避免緩存 const response = await fetch(`/api/admin/teams?t=${Date.now()}`, { cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }) const data = await response.json() console.log('🔍 fetchTeams 響應:', data) if (data.success) { console.log('📊 獲取到的團隊數據:', data.data) setDbTeams(data.data) } else { setError('獲取團隊列表失敗: ' + data.message) } } catch (error) { console.error('獲取團隊列表失敗:', error) setError('獲取團隊列表失敗') } finally { setIsLoadingTeams(false) } } const fetchTeamStats = async () => { try { const response = await fetch('/api/admin/teams/stats') const data = await response.json() console.log('團隊統計 API 響應:', data) if (data.success) { setTeamStats(data.data) console.log('團隊統計設置成功:', data.data) } } catch (error) { console.error('獲取團隊統計失敗:', error) } } const createTeamInDb = async (teamData: any) => { try { const response = await fetch('/api/admin/teams', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(teamData) }) const data = await response.json() if (data.success) { setSuccess('團隊創建成功!') await fetchTeams() // 重新獲取列表 await fetchTeamStats() // 重新獲取統計 return data.data } else { setError('創建團隊失敗: ' + data.message) return null } } catch (error) { console.error('創建團隊失敗:', error) setError('創建團隊失敗') return null } } const updateTeamInDb = async (teamId: string, updates: any) => { try { const response = await fetch(`/api/admin/teams/${teamId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(updates) }) const data = await response.json() if (data.success) { setSuccess('團隊更新成功!') await fetchTeams() // 重新獲取列表 await fetchTeamStats() // 重新獲取統計 return true // 返回 true 表示成功 } else { setError('更新團隊失敗: ' + data.message) return false } } catch (error) { console.error('更新團隊失敗:', error) setError('更新團隊失敗') return false } } const deleteTeamInDb = async (teamId: string, hard: boolean = false, skipRefresh: boolean = false) => { try { const response = await fetch(`/api/admin/teams/${teamId}?hard=${hard}`, { method: 'DELETE' }) const data = await response.json() if (data.success) { setSuccess(data.message || '團隊刪除成功!') if (!skipRefresh) { await fetchTeams() // 重新獲取列表 await fetchTeamStats() // 重新獲取統計 } return true } else { setError('刪除團隊失敗: ' + data.message) return false } } catch (error) { console.error('刪除團隊失敗:', error) setError('刪除團隊失敗') return false } } // 獲取可用應用列表 const fetchAvailableApps = async (teamId?: string) => { console.log('🔍 開始獲取可用應用列表, teamId:', teamId) setIsLoadingApps(true) try { const url = teamId ? `/api/admin/apps/available?teamId=${teamId}` : '/api/admin/apps/available' console.log('📡 請求 URL:', url) const response = await fetch(url) const data = await response.json() console.log('📊 應用 API 響應:', data) if (data.success) { console.log('✅ 應用數據設置成功:', data.data.length, '個應用') setAvailableApps(data.data) return data.data // 返回數據 } else { console.error('❌ 獲取應用列表失敗:', data.message) setError('獲取可用應用列表失敗: ' + data.message) return [] } } catch (error) { console.error('❌ 獲取可用應用列表失敗:', error) setError('獲取可用應用列表失敗') return [] } finally { setIsLoadingApps(false) } } // 獲取可用用戶列表 const fetchAvailableUsers = async () => { console.log('🔍 開始獲取可用用戶列表') setIsLoadingUsers(true) try { const response = await fetch('/api/admin/users/available') const data = await response.json() console.log('📊 用戶 API 響應:', data) if (data.success) { console.log('✅ 用戶數據設置成功:', data.data.length, '個用戶') setAvailableUsers(data.data) return data.data // 返回數據 } else { console.error('❌ 獲取用戶列表失敗:', data.message) setError('獲取可用用戶列表失敗: ' + data.message) return [] } } catch (error) { console.error('❌ 獲取可用用戶列表失敗:', error) setError('獲取可用用戶列表失敗') return [] } finally { setIsLoadingUsers(false) } } // 獲取完整團隊信息 const fetchTeamDetails = async (teamId: string) => { try { const response = await fetch(`/api/admin/teams/${teamId}`) const data = await response.json() if (data.success) { return data.data } else { setError('獲取團隊詳情失敗: ' + data.message) return null } } catch (error) { console.error('獲取團隊詳情失敗:', error) setError('獲取團隊詳情失敗') return null } } // 編輯團隊 const handleEditTeam = async (team: any) => { try { console.log('🔍 開始編輯團隊:', team.id) // 先加載用戶和應用數據 const users = await fetchAvailableUsers() const apps = await fetchAvailableApps(team.id) console.log('📊 實際載入的用戶數據:', users.length) console.log('📊 實際載入的應用數據:', apps.length) // 獲取完整的團隊信息 const teamDetails = await fetchTeamDetails(team.id) console.log('📋 團隊詳情:', teamDetails) if (teamDetails) { // 填充表單數據 const teamData = { name: teamDetails.name, leader: teamDetails.leader_name || '', leader_id: teamDetails.leader_id || '', department: teamDetails.department, contactEmail: teamDetails.contact_email || '', leaderPhone: teamDetails.leader_phone || '', description: teamDetails.description || '', members: teamDetails.members || [], apps: teamDetails.apps ? teamDetails.apps.map((app: any) => app.id || app) : [], submittedAppCount: teamDetails.apps?.length || 0, } console.log('📝 填充的表單數據:', teamData) setNewTeam(teamData) setSelectedTeam(teamDetails) setShowCreateTeam(true) } } catch (error) { console.error('編輯團隊失敗:', error) setError('編輯團隊失敗') } } // 刪除團隊 const handleDeleteTeam = async (team: any) => { setTeamToDelete(team) setShowDeleteTeamConfirm(true) } // 確認刪除團隊 const confirmDeleteTeam = async () => { if (!teamToDelete) return setIsLoading(true) try { const success = await deleteTeamInDb(teamToDelete.id) if (success) { setSuccess("團隊刪除成功!") setShowDeleteTeamConfirm(false) setTeamToDelete(null) // 重新獲取團隊列表 await fetchTeams() await fetchTeamStats() } } catch (error) { console.error('刪除團隊失敗:', error) setError('刪除團隊失敗') } finally { setIsLoading(false) } } const createJudgeInDb = async (judgeData: any) => { try { const response = await fetch('/api/admin/judges', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(judgeData) }) const data = await response.json() if (data.success) { setSuccess('評審創建成功!') await fetchJudges() // 重新獲取列表 await fetchJudgeStats() // 重新獲取統計 return data.data } else { setError('創建評審失敗: ' + data.message) return null } } catch (error) { console.error('創建評審失敗:', error) setError('創建評審失敗') return null } } const updateJudgeInDb = async (id: string, updates: any) => { try { const response = await fetch(`/api/admin/judges/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(updates) }) const data = await response.json() if (data.success) { setSuccess('評審更新成功!') await fetchJudges() // 重新獲取列表 await fetchJudgeStats() // 重新獲取統計 return true // 返回 true 表示成功 } else { setError('更新評審失敗: ' + data.message) return false } } catch (error) { console.error('更新評審失敗:', error) setError('更新評審失敗') return false } } const deleteJudgeInDb = async (id: string, hardDelete: boolean = false, skipRefresh: boolean = false) => { try { const url = hardDelete ? `/api/admin/judges/${id}?hard=true` : `/api/admin/judges/${id}` const response = await fetch(url, { method: 'DELETE' }) const data = await response.json() if (data.success) { setSuccess(data.message || '評審刪除成功!') if (!skipRefresh) { await fetchJudges() // 重新獲取列表 await fetchJudgeStats() // 重新獲取統計 } return true } else { setError('刪除評審失敗: ' + data.message) return false } } catch (error) { console.error('刪除評審失敗:', error) setError('刪除評審失敗') return false } } const [awardMonthFilter, setAwardMonthFilter] = useState("all") const [awardTypeFilter, setAwardTypeFilter] = useState("all") const [awardCompetitionTypeFilter, setAwardCompetitionTypeFilter] = useState("all") // 組件載入時獲取資料 useEffect(() => { fetchCompetitions() fetchCompetitionStats() fetchJudges() fetchJudgeStats() fetchTeams() fetchTeamStats() }, []) // 当筛选条件改变时重置分页 const resetAwardPagination = () => { setAwardCurrentPage(1) } // Manual scoring states const [manualScoring, setManualScoring] = useState({ judgeId: "", participantId: "", participantType: "individual" as "individual" | "team", scores: {} as Record, comments: "", }) // 混合賽的參賽者類型選擇 const [selectedParticipantType, setSelectedParticipantType] = useState<"individual" | "team">("individual") // Form states - Updated for mixed competition support const [newCompetition, setNewCompetition] = useState({ name: "", type: "individual" as "individual" | "team" | "mixed", year: new Date().getFullYear(), month: new Date().getMonth() + 1, startDate: "", endDate: "", description: "", status: "upcoming" as const, // For individual and team competitions judges: [] as string[], participatingApps: [] as string[], participatingTeams: [] as string[], evaluationFocus: "", rules: [] as CompetitionRule[], awardTypes: [] as CompetitionAwardType[], // For mixed competitions - separate configurations individualConfig: { judges: [] as string[], rules: [] as CompetitionRule[], awardTypes: [] as CompetitionAwardType[], evaluationFocus: "", }, teamConfig: { judges: [] as string[], rules: [] as CompetitionRule[], awardTypes: [] as CompetitionAwardType[], evaluationFocus: "", }, }) const [newJudge, setNewJudge] = useState({ name: "", title: "", department: "", expertise: "", }) const [newAward, setNewAward] = useState({ competitionId: "", participantId: "", participantType: "individual" as "individual" | "team", awardType: "custom" as const, awardName: "", customAwardTypeId: "", description: "", score: 0, category: "innovation" as const, rank: 0, applicationLinks: { production: "", demo: "", github: "", }, documents: [] as { id: string; name: string; type: string; size: string; uploadDate: string; url: string }[], judgeComments: "", photos: [] as { id: string; name: string; url: string; caption: string; uploadDate: string; size: string }[], }) // Team form states const [newTeam, setNewTeam] = useState({ name: "", leader: "", leader_id: "", // 添加隊長 ID department: "HQBU", contactEmail: "", leaderPhone: "", description: "", members: [] as Array<{ id: string; name: string; department: string; role: string }>, apps: [] as string[], // 改為存儲應用 ID submittedAppCount: 0, }) const [newMember, setNewMember] = useState({ name: "", user_id: "", // 添加用戶 ID department: "HQBU", role: "成員", }) const [newApp, setNewApp] = useState({ name: "", link: "", }) const [createError, setCreateError] = useState("") // Participant selection states - separate for mixed competitions const [participantSearchTerm, setParticipantSearchTerm] = useState("") const [departmentFilter, setDepartmentFilter] = useState("all") const [individualParticipantSearchTerm, setIndividualParticipantSearchTerm] = useState("") const [teamParticipantSearchTerm, setTeamParticipantSearchTerm] = useState("") const [individualDepartmentFilter, setIndividualDepartmentFilter] = useState("all") const [teamDepartmentFilter, setTeamDepartmentFilter] = useState("all") // Get participants based on competition type const [selectedJudge, setSelectedJudge] = useState(null) const [showJudgeDetail, setShowJudgeDetail] = useState(false) const [showDeleteJudgeConfirm, setShowDeleteJudgeConfirm] = useState(false) const [showDisableJudgeConfirm, setShowDisableJudgeConfirm] = useState(false) // 獎項相關狀態 const [showAwardDetail, setShowAwardDetail] = useState(false) const [selectedAward, setSelectedAward] = useState(null) const [showDeleteAwardConfirm, setShowDeleteAwardConfirm] = useState(false) const [awardToDelete, setAwardToDelete] = useState(null) // 評審分頁和篩選狀態 const [judgeCurrentPage, setJudgeCurrentPage] = useState(1) const [judgeSearchTerm, setJudgeSearchTerm] = useState("") const [judgeDepartmentFilter, setJudgeDepartmentFilter] = useState("all") const [judgeExpertiseFilter, setJudgeExpertiseFilter] = useState("all") const [judgeStatusFilter, setJudgeStatusFilter] = useState("all") const judgesPerPage = 6 // 團隊分頁和篩選狀態 const [teamCurrentPage, setTeamCurrentPage] = useState(1) const [teamSearchTerm, setTeamSearchTerm] = useState("") const teamsPerPage = 6 // 獎項分頁狀態 const [awardCurrentPage, setAwardCurrentPage] = useState(1) const awardsPerPage = 6 // Get participants based on competition type const getParticipants = (competitionType: string) => { switch (competitionType) { case "individual": return mockIndividualApps case "team": return dbTeams.length > 0 ? dbTeams : teams default: return [] } } // Filter participants - updated for mixed competitions const getFilteredParticipants = (competitionType: string) => { const participants = getParticipants(competitionType) let searchTerm = participantSearchTerm let departmentFilterValue = departmentFilter // Use separate search terms for mixed competitions if (newCompetition.type === "mixed") { searchTerm = competitionType === "individual" ? individualParticipantSearchTerm : teamParticipantSearchTerm departmentFilterValue = competitionType === "individual" ? individualDepartmentFilter : teamDepartmentFilter } return participants.filter((participant) => { const searchField = competitionType === "team" ? participant.name : participant.name const creatorField = competitionType === "team" ? participant.leader : participant.creator const matchesSearch = searchField.toLowerCase().includes(searchTerm.toLowerCase()) || creatorField.toLowerCase().includes(searchTerm.toLowerCase()) const matchesDepartment = departmentFilterValue === "all" || participant.department === departmentFilterValue return matchesSearch && matchesDepartment }) } const resetForm = () => { setNewCompetition({ name: "", type: "individual", year: new Date().getFullYear(), month: new Date().getMonth() + 1, startDate: "", endDate: "", description: "", status: "upcoming", judges: [], participatingApps: [], participatingTeams: [], evaluationFocus: "", rules: [], awardTypes: [], individualConfig: { judges: [], rules: [], awardTypes: [], evaluationFocus: "", }, teamConfig: { judges: [], rules: [], awardTypes: [], evaluationFocus: "", }, }) // Reset search terms setParticipantSearchTerm("") setDepartmentFilter("all") setIndividualParticipantSearchTerm("") setTeamParticipantSearchTerm("") setIndividualDepartmentFilter("all") setTeamDepartmentFilter("all") } const resetTeamForm = () => { setNewTeam({ name: "", leader: "", department: "HQBU", contactEmail: "", leaderPhone: "", description: "", members: [], apps: [], submittedAppCount: 0, }) setNewMember({ name: "", department: "HQBU", role: "成員", }) setNewApp({ name: "", link: "", }) } const handleCreateCompetition = async () => { setCreateError("") if (!newCompetition.name || !newCompetition.startDate || !newCompetition.endDate) { setCreateError("請填寫所有必填欄位") return } // Validation for mixed competitions if (newCompetition.type === "mixed") { if (newCompetition.individualConfig.judges.length === 0 && newCompetition.teamConfig.judges.length === 0) { setCreateError("混合賽至少需要為個人賽或團體賽選擇評審") return } // Check if at least one competition type has participants const hasParticipants = newCompetition.participatingApps.length > 0 || newCompetition.participatingTeams.length > 0 if (!hasParticipants) { setCreateError("請至少選擇一個個人賽應用或團隊賽團隊") return } // Validate individual rules if there are individual participants and judges if (newCompetition.participatingApps.length > 0 && newCompetition.individualConfig.judges.length > 0) { if (newCompetition.individualConfig.rules.length > 0) { const individualTotalWeight = newCompetition.individualConfig.rules.reduce( (sum, rule) => sum + rule.weight, 0, ) if (individualTotalWeight !== 100) { setCreateError("個人賽評比標準權重總和必須為 100%") return } } } // Validate team rules if there are team participants and judges if (newCompetition.participatingTeams.length > 0 && newCompetition.teamConfig.judges.length > 0) { if (newCompetition.teamConfig.rules.length > 0) { const teamTotalWeight = newCompetition.teamConfig.rules.reduce((sum, rule) => sum + rule.weight, 0) if (teamTotalWeight !== 100) { setCreateError("團體賽評比標準權重總和必須為 100%") return } } } } else { // Validation for single type competitions if (newCompetition.judges.length === 0) { setCreateError("請至少選擇一位評審") return } const hasParticipants = (newCompetition.type === "individual" && newCompetition.participatingApps.length > 0) || (newCompetition.type === "team" && newCompetition.participatingTeams.length > 0) if (!hasParticipants) { setCreateError("請至少選擇一個參賽項目") return } if (newCompetition.rules.length > 0) { const totalWeight = newCompetition.rules.reduce((sum, rule) => sum + rule.weight, 0) if (totalWeight !== 100) { setCreateError("評比標準權重總和必須為 100%") return } const hasEmptyRule = newCompetition.rules.some((rule) => !rule.name.trim() || !rule.description.trim()) if (hasEmptyRule) { setCreateError("請填寫所有評比標準的名稱和描述") return } } if (newCompetition.awardTypes.length > 0) { const hasEmptyAwardType = newCompetition.awardTypes.some( (awardType) => !awardType.name.trim() || !awardType.description.trim(), ) if (hasEmptyAwardType) { setCreateError("請填寫所有獎項類型的名稱和描述") return } } } setIsLoading(true) try { if (selectedCompetitionForAction) { // 編輯模式 - 更新現有競賽 const updates = { name: newCompetition.name, year: newCompetition.year, month: newCompetition.month, startDate: newCompetition.startDate, endDate: newCompetition.endDate, status: newCompetition.status, description: newCompetition.description, type: newCompetition.type, evaluationFocus: newCompetition.evaluationFocus, maxTeamSize: newCompetition.maxTeamSize || null, // 關聯數據 judges: newCompetition.judges || [], teams: newCompetition.participatingTeams || [], awardTypes: newCompetition.awardTypes || [], rules: newCompetition.rules || [] } const updatedCompetition = await updateCompetitionInDb(selectedCompetitionForAction.id, updates) if (updatedCompetition) { // 同時更新 context updateCompetition(selectedCompetitionForAction.id, newCompetition) } } else { // 創建模式 - 新增競賽到資料庫 const competitionData = { name: newCompetition.name, year: newCompetition.year, month: newCompetition.month, startDate: newCompetition.startDate, endDate: newCompetition.endDate, status: newCompetition.status, description: newCompetition.description, type: newCompetition.type, evaluationFocus: newCompetition.evaluationFocus, maxTeamSize: newCompetition.maxTeamSize || null, isActive: true, // 關聯數據 judges: newCompetition.judges || [], teams: newCompetition.participatingTeams || [], awardTypes: newCompetition.awardTypes || [], rules: newCompetition.rules || [] } const createdCompetition = await createCompetitionInDb(competitionData) if (createdCompetition) { // 同時添加到 context(保持向後兼容) const competitionWithId = { ...newCompetition, id: createdCompetition.id, createdAt: createdCompetition.created_at, } addCompetition(competitionWithId) } } } catch (error) { console.error("處理競賽失敗:", error) setCreateError("處理競賽時發生錯誤") } setShowCreateCompetition(false) setSelectedCompetitionForAction(null) setCreateError("") resetForm() setIsLoading(false) setTimeout(() => setSuccess(""), 3000) } const handleCreateTeam = async () => { setCreateError("") if (!newTeam.name || !newTeam.leader || !newTeam.contactEmail) { setCreateError("請填寫團隊名稱、隊長和聯絡信箱") return } if (newTeam.members.length === 0) { setCreateError("請至少添加一名團隊成員") return } // Check if leader is in members list const leaderInMembers = newTeam.members.some((member) => member.name === newTeam.leader) if (!leaderInMembers) { setCreateError("隊長必須在團隊成員列表中") return } setIsLoading(true) try { if (selectedTeam) { // 編輯模式 - 更新現有團隊 // 使用選擇的隊長 ID,如果沒有選擇則保持原有的隊長 ID const leaderId = newTeam.leader_id || (selectedTeam as any).leader_id || '0b844fb6-1a63-4e0c-a15a-416e9b0ec8c7' const teamData = { name: newTeam.name, leader_id: leaderId, department: newTeam.department, contact_email: newTeam.contactEmail, description: newTeam.description, members: newTeam.members.map(member => ({ user_id: member.id, // 現在 member.id 就是 user_id role: member.role || 'member' })), apps: newTeam.apps // 添加應用 ID 列表 } const success = await updateTeamInDb(selectedTeam.id, teamData) if (success) { setSuccess("團隊更新成功!") } } else { // 創建模式 - 新增團隊 // 使用選擇的隊長 ID,如果沒有選擇則使用預設的用戶 ID const leaderId = newTeam.leader_id || '0b844fb6-1a63-4e0c-a15a-416e9b0ec8c7' const teamData = { name: newTeam.name, leader_id: leaderId, department: newTeam.department, contact_email: newTeam.contactEmail, description: newTeam.description, members: newTeam.members.map(member => ({ user_id: member.id, // 現在 member.id 就是 user_id role: member.role || 'member' })), apps: newTeam.apps // 添加應用 ID 列表 } const createdTeam = await createTeamInDb(teamData) if (createdTeam) { setSuccess("團隊創建成功!") } } } catch (error) { console.error("處理團隊失敗:", error) setCreateError("處理團隊時發生錯誤") } setShowCreateTeam(false) setSelectedTeam(null) resetTeamForm() setIsLoading(false) setTimeout(() => setSuccess(""), 3000) } const handleConfirmDeleteTeam = async () => { if (!teamToDelete) return setIsLoading(true) try { const success = await deleteTeamInDb(teamToDelete.id, true) // 硬刪除 if (success) { setShowDeleteTeamConfirm(false) setTeamToDelete(null) setSuccess("團隊刪除成功!") } else { setError("刪除團隊失敗") } } catch (error) { console.error('刪除團隊失敗:', error) setError('刪除團隊失敗') } finally { setIsLoading(false) } } const handleAddMember = () => { if (!newMember.user_id || !newMember.name.trim()) { setCreateError("請選擇成員") return } // 檢查是否已經添加過這個成員 if (newTeam.members.some(member => member.id === newMember.user_id)) { setCreateError("該成員已經在團隊中") return } const member = { id: newMember.user_id, // 使用用戶 ID name: newMember.name, department: newMember.department, role: newMember.role, } setNewTeam({ ...newTeam, members: [...newTeam.members, member], }) setNewMember({ name: "", user_id: "", department: "HQBU", role: "成員", }) setCreateError("") } const handleRemoveMember = (memberId: string) => { setNewTeam({ ...newTeam, members: newTeam.members.filter((m) => m.id !== memberId), }) } const handleAddApp = (appId: string) => { if (newTeam.apps.includes(appId)) { setCreateError("此應用已經加入團隊") return } setNewTeam({ ...newTeam, apps: [...newTeam.apps, appId], }) setCreateError("") } const handleRemoveApp = (appId: string) => { setNewTeam({ ...newTeam, apps: newTeam.apps.filter(id => id !== appId), }) } const handleAddJudge = async () => { setError("") if (!newJudge.name || !newJudge.title || !newJudge.department) { setError("請填寫所有必填欄位") return } setIsLoading(true) try { const expertiseArray = newJudge.expertise .split(",") .map((s) => s.trim()) .filter(Boolean) if (selectedJudge) { // 編輯模式 - 更新現有評審 const updates = { name: newJudge.name, title: newJudge.title, department: newJudge.department, expertise: expertiseArray, avatar: newJudge.avatar || null, isActive: true } const updatedJudge = await updateJudgeInDb(selectedJudge.id, updates) if (updatedJudge) { // 同時更新 context updateJudge(selectedJudge.id, { ...newJudge, expertise: expertiseArray, }) } } else { // 新增模式 - 新增評審到資料庫 const judgeData = { name: newJudge.name, title: newJudge.title, department: newJudge.department, expertise: expertiseArray, avatar: newJudge.avatar || null, isActive: true } const createdJudge = await createJudgeInDb(judgeData) if (createdJudge) { // 同時添加到 context(保持向後兼容) addJudge({ ...newJudge, expertise: expertiseArray, }) // 重新獲取評審列表和統計 await fetchJudges() await fetchJudgeStats() } } } catch (error) { console.error("處理評審失敗:", error) setError("處理評審時發生錯誤") } setShowAddJudge(false) setSelectedJudge(null) setNewJudge({ name: "", title: "", department: "", expertise: "", }) setIsLoading(false) setTimeout(() => setSuccess(""), 3000) } const handleEditJudge = (judge: any) => { setSelectedJudge(judge) setNewJudge({ name: judge.name, title: judge.title, department: judge.department, expertise: judge.expertise.join(", "), }) setShowAddJudge(true) // 使用新增評審對話框 } const handleDisableJudge = (judge: any) => { setSelectedJudge(judge) setShowDisableJudgeConfirm(true) } const handleEnableJudge = async (judge: any) => { setIsLoading(true) try { const success = await updateJudgeInDb(judge.id, { is_active: true }) if (success) { setSuccess('評審已啟用!') // 不需要再次調用 fetchJudges 和 fetchJudgeStats,因為 updateJudgeInDb 已經調用了 } else { setError('啟用評審失敗') } } catch (error) { console.error('啟用評審失敗:', error) setError('啟用評審失敗') } finally { setIsLoading(false) } } const handleDeleteJudge = (judge: any) => { setSelectedJudge(judge) setShowDeleteJudgeConfirm(true) } const confirmDisableJudge = async () => { if (!selectedJudge) return setIsLoading(true) try { const success = await updateJudgeInDb(selectedJudge.id, { is_active: false }) if (success) { setShowDisableJudgeConfirm(false) setSelectedJudge(null) setSuccess('評審已停用!') // 不需要再次調用 fetchJudges 和 fetchJudgeStats,因為 updateJudgeInDb 已經調用了 } else { setError('停用評審失敗') } } catch (error) { console.error('停用評審失敗:', error) setError('停用評審失敗') } finally { setIsLoading(false) } } const confirmDeleteJudge = async () => { if (!selectedJudge) return const judgeId = selectedJudge.id // 保存 ID,避免後續變為 null const judgeName = selectedJudge.name // 保存名稱,避免後續變為 null setIsLoading(true) try { const success = await deleteJudgeInDb(judgeId, true, true) // 直接硬刪除,跳過內部刷新 if (success) { setShowDeleteJudgeConfirm(false) setSelectedJudge(null) // 立即從 context 中移除(如果存在) console.log('🗑️ 調用 deleteJudge 移除評審:', judgeId) console.log('🗑️ 評審名稱:', judgeName) console.log('🗑️ 當前 context 中的評審:', judges.map(j => ({ id: j.id, name: j.name }))) // 根據名稱匹配 context 中的評審,因為 ID 可能不匹配 const contextJudge = judges.find(j => j.name === judgeName) if (contextJudge) { console.log('🗑️ 找到匹配的 context 評審:', contextJudge) deleteJudge(contextJudge.id) } else { console.log('🗑️ 沒有找到匹配的 context 評審,嘗試使用原始 ID') deleteJudge(judgeId) } // 強制從 dbJudges 中移除,確保立即更新 setDbJudges(prev => { console.log('🗑️ 從 dbJudges 中移除評審:', judgeId) console.log('🗑️ 移除前的評審列表:', prev.map(j => ({ id: j.id, name: j.name, is_active: j.is_active }))) const filtered = prev.filter(judge => judge.id !== judgeId) console.log('🗑️ 移除後的評審列表:', filtered.map(j => ({ id: j.id, name: j.name, is_active: j.is_active }))) return filtered }) // 等待一個微任務,確保 context 狀態更新完成 await new Promise(resolve => setTimeout(resolve, 0)) // 確保列表和統計數據立即更新 console.log('🔄 調用 fetchJudges 重新獲取數據') await fetchJudges() await fetchJudgeStats() // 再次檢查 context 狀態 console.log('🔍 刪除後檢查 context 狀態') } } catch (error) { console.error('刪除評審失敗:', error) setError('刪除評審失敗') } finally { setIsLoading(false) } } const handleCreateAward = async () => { setError("") if (!newAward.competitionId || !newAward.participantId || !newAward.awardName) { setError("請填寫所有必填欄位") return } setIsLoading(true) await new Promise((resolve) => setTimeout(resolve, 1000)) const competition = competitions.find((c) => c.id === newAward.competitionId) let participant: any = null let participantName = "" let creatorName = "" // Get participant based on type if (newAward.participantType === "individual") { // 示例個人應用數據 const mockIndividualApps = [ { id: "app1", name: "智能客服系統", creator: "張小明", department: "ITBU" }, { id: "app2", name: "數據分析平台", creator: "李美華", department: "研發部" }, ] participant = mockIndividualApps.find((a) => a.id === newAward.participantId) participantName = participant?.name || "" creatorName = participant?.creator || "" } else if (newAward.participantType === "team") { participant = (dbTeams.length > 0 ? dbTeams : teams).find((t) => t.id === newAward.participantId) participantName = participant?.name || "" creatorName = participant?.leader || "" } if (competition && participant) { // 根據獎項類型設定圖標 let icon = "🏆" switch (newAward.awardType) { case "gold": icon = "🥇"; break; case "silver": icon = "🥈"; break; case "bronze": icon = "🥉"; break; case "popular": icon = "👥"; break; case "innovation": icon = "💡"; break; case "technical": icon = "⚙️"; break; default: icon = "🏆"; break; } const award = { id: `award_${Date.now()}`, competitionId: newAward.competitionId, appId: newAward.participantType === "individual" ? newAward.participantId : undefined, teamId: newAward.participantType === "team" ? newAward.participantId : undefined, appName: newAward.participantType === "individual" ? participantName : undefined, creator: creatorName, awardType: newAward.awardType, awardName: newAward.awardName, score: newAward.score, year: competition.year, month: competition.month, icon, rank: newAward.rank, category: newAward.category, competitionType: newAward.participantType, description: newAward.description, judgeComments: newAward.judgeComments, applicationLinks: newAward.applicationLinks, documents: newAward.documents, photos: newAward.photos, } addAward(award) } setShowCreateAward(false) setNewAward({ competitionId: "", participantId: "", participantType: "individual", awardType: "custom", awardName: "", customAwardTypeId: "", description: "", score: 0, category: "innovation", rank: 0, applicationLinks: { production: "", demo: "", github: "", }, documents: [], judgeComments: "", photos: [], }) setSuccess(selectedAward ? "獎項更新成功!" : "獎項創建成功!") setIsLoading(false) setTimeout(() => setSuccess(""), 3000) } const handleViewAward = (award: any) => { setSelectedAward(award) setShowAwardDetail(true) } const handleEditAward = (award: any) => { setSelectedAward(award) setNewAward({ competitionId: award.competitionId, participantId: award.appId || award.teamId || "", participantType: award.competitionType, awardType: award.awardType, awardName: award.awardName, customAwardTypeId: award.customAwardTypeId || "", description: (award as any).description || "", score: award.score, category: award.category, rank: award.rank, applicationLinks: (award as any).applicationLinks || { production: "", demo: "", github: "", }, documents: (award as any).documents || [], judgeComments: (award as any).judgeComments || "", photos: (award as any).photos || [], }) setShowCreateAward(true) } const handleDeleteAward = (award: any) => { setAwardToDelete(award) setShowDeleteAwardConfirm(true) } const confirmDeleteAward = async () => { if (!awardToDelete) return setIsLoading(true) await new Promise((resolve) => setTimeout(resolve, 500)) // 這裡應該調用 context 中的刪除函數 // deleteAward(awardToDelete.id) setShowDeleteAwardConfirm(false) setAwardToDelete(null) setSuccess("獎項刪除成功!") setIsLoading(false) setTimeout(() => setSuccess(""), 3000) } const handleManualScoring = (competition: any) => { setSelectedCompetition(competition) // 設定初始參賽者類型 if (competition.type === "mixed") { setSelectedParticipantType("individual") // 混合賽預設從個人賽開始 } else { setSelectedParticipantType(competition.type) } // 初始化評分項目 const initialScores = getInitialScores(competition, competition.type === "mixed" ? "individual" : competition.type) setManualScoring({ judgeId: "", participantId: "", participantType: competition.type || "individual", scores: initialScores, comments: "", }) setShowManualScoring(true) } // 獲取初始評分項目的輔助函數 const getInitialScores = (competition: any, participantType: "individual" | "team") => { const initialScores: Record = {} if (competition.type === "mixed") { // 混合賽:根據參賽者類型選擇對應的評分規則 const config = participantType === "individual" ? competition.individualConfig : competition.teamConfig if (config && config.rules && config.rules.length > 0) { config.rules.forEach((rule: any) => { initialScores[rule.name] = 0 }) } else { // 預設評分項目 getDefaultScoringItems(participantType).forEach(item => { initialScores[item.name] = 0 }) } } else { // 單一類型競賽 if (competition.rules && competition.rules.length > 0) { competition.rules.forEach((rule: any) => { initialScores[rule.name] = 0 }) } else { // 預設評分項目 getDefaultScoringItems(participantType).forEach(item => { initialScores[item.name] = 0 }) } } return initialScores } // 獲取預設評分項目 const getDefaultScoringItems = (participantType: "individual" | "team") => { if (participantType === "team") { return [ { name: '團隊合作', description: '團隊協作和溝通能力' }, { name: '創新性', description: '創新程度和獨特性' }, { name: '技術性', description: '技術實現的複雜度和品質' }, { name: '實用性', description: '實際應用價值和用戶體驗' }, { name: '展示效果', description: '團隊展示的清晰度和吸引力' } ] } else { return [ { name: '創新性', description: '創新程度和獨特性' }, { name: '技術性', description: '技術實現的複雜度和品質' }, { name: '實用性', description: '實際應用價值和用戶體驗' }, { name: '展示效果', description: '展示的清晰度和吸引力' }, { name: '影響力', description: '對行業或社會的潛在影響' } ] } } // 處理參賽者類型變更(僅針對混合賽) const handleParticipantTypeChange = (newType: "individual" | "team") => { setSelectedParticipantType(newType) // 重新初始化評分項目 const newScores = getInitialScores(selectedCompetition, newType) setManualScoring({ ...manualScoring, participantId: "", // 清空選擇的參賽者 scores: newScores, }) } const handleSubmitManualScore = async () => { setError("") if (!manualScoring.judgeId || !manualScoring.participantId) { setError("請選擇評審和參賽項目") return } const hasAllScores = Object.values(manualScoring.scores).every((score) => score > 0) if (!hasAllScores) { setError("請為所有評分項目打分") return } if (!manualScoring.comments.trim()) { setError("請填寫評審意見") return } setIsLoading(true) await new Promise((resolve) => setTimeout(resolve, 1000)) submitJudgeScore({ judgeId: manualScoring.judgeId, appId: manualScoring.participantId, // Using appId field for all participant types scores: manualScoring.scores, comments: manualScoring.comments.trim(), }) setManualScoring({ judgeId: "", participantId: "", participantType: "individual", scores: { innovation: 0, technical: 0, usability: 0, presentation: 0, impact: 0, }, comments: "", }) setSuccess("評分提交成功!") setIsLoading(false) setTimeout(() => setSuccess(""), 3000) } const handleViewCompetition = (competition: any) => { setSelectedCompetitionForAction(competition) setShowCompetitionDetail(true) } const handleEditCompetition = (competition: any) => { setSelectedCompetitionForAction(competition) setNewCompetition({ name: competition.name, type: competition.type, year: competition.year, month: competition.month, startDate: competition.startDate, endDate: competition.endDate, description: competition.description, status: competition.status, judges: competition.judges || [], participatingApps: competition.participatingApps || [], participatingTeams: competition.participatingTeams || [], evaluationFocus: competition.evaluationFocus || "", rules: competition.rules || [], awardTypes: competition.awardTypes || [], individualConfig: competition.individualConfig || { judges: [], rules: [], awardTypes: [], evaluationFocus: "", }, teamConfig: competition.teamConfig || { judges: [], rules: [], awardTypes: [], evaluationFocus: "", }, }) setShowCreateCompetition(true) // 使用創建競賽對話框 } const handleDeleteCompetition = (competition: any) => { setSelectedCompetitionForAction(competition) setShowDeleteCompetitionConfirm(true) } const handleChangeStatus = (competition: any) => { setSelectedCompetitionForAction(competition) setNewStatus(competition.status) setShowChangeStatusDialog(true) } const confirmDeleteCompetition = async () => { if (!selectedCompetitionForAction) return setIsLoading(true) await new Promise((resolve) => setTimeout(resolve, 500)) deleteCompetition(selectedCompetitionForAction.id) setShowDeleteCompetitionConfirm(false) setSelectedCompetitionForAction(null) setSuccess("競賽刪除成功!") setIsLoading(false) setTimeout(() => setSuccess(""), 3000) } const handleUpdateStatus = async () => { if (!selectedCompetitionForAction) return setIsLoading(true) await new Promise((resolve) => setTimeout(resolve, 500)) updateCompetition(selectedCompetitionForAction.id, { ...selectedCompetitionForAction, status: newStatus, }) setShowChangeStatusDialog(false) setSelectedCompetitionForAction(null) setSuccess("競賽狀態更新成功!") setIsLoading(false) setTimeout(() => setSuccess(""), 3000) } const getCompetitionTypeIcon = (type: string) => { switch (type) { case "individual": return case "team": return case "mixed": return default: return } } const getScoreLabelText = (key: string) => { switch (key) { case "innovation": return "創新性" case "technical": return "技術性" case "usability": return "實用性" case "presentation": return "展示性" case "impact": return "影響力" default: return key } } const getCompetitionTypeText = (type: string) => { switch (type) { case "individual": return "個人賽" case "team": return "團體賽" case "mixed": return "混合賽" default: return "未知類型" } } // 格式化時間顯示 const formatDateRange = (startDate: string, endDate: string) => { try { const start = new Date(startDate) const end = new Date(endDate) const formatDate = (date: Date) => { return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}` } return `${formatDate(start)} ~ ${formatDate(end)}` } catch (error) { return `${startDate} ~ ${endDate}` } } // 計算權重百分比 const calculateWeightPercentage = (weight: any) => { const weightNum = parseFloat(weight) || 0; // 如果權重已經是百分比格式(大於1),直接使用 if (weightNum > 1) { return weightNum; } // 如果權重是小數格式(0-1),轉換為百分比 return weightNum * 100; } // 計算總權重 const calculateTotalWeight = (rules: any[]) => { return rules.reduce((sum, rule) => { return sum + calculateWeightPercentage(rule.weight); }, 0); } const getParticipantCount = (competition: any) => { // 如果是從資料庫載入的競賽,使用 teams 屬性 if (competition.teams && Array.isArray(competition.teams)) { return competition.teams.length } // 如果是本地競賽數據,使用原來的邏輯 switch (competition.type) { case "individual": return competition.participatingApps?.length || 0 case "team": return competition.participatingTeams?.length || 0 case "mixed": return (competition.participatingApps?.length || 0) + (competition.participatingTeams?.length || 0) default: return 0 } } const getScoringProgress = (competitionId: string) => { const competition = competitions.find((c) => c.id === competitionId) if (!competition) return { completed: 0, total: 0, percentage: 0 } const participantCount = getParticipantCount(competition) const totalExpected = competition.judges.length * participantCount const completed = judgeScores.filter((score) => { const individualParticipants = competition.participatingApps || [] const teamParticipants = competition.participatingTeams || [] const allParticipants = [...individualParticipants, ...teamParticipants] return allParticipants.includes(score.appId) && competition.judges.includes(score.judgeId) }).length return { completed, total: totalExpected, percentage: totalExpected > 0 ? Math.round((completed / totalExpected) * 100) : 0, } } const getStatusColor = (status: string) => { switch (status) { case "completed": return "bg-green-100 text-green-800 border-green-200" case "active": return "bg-blue-100 text-blue-800 border-blue-200" case "judging": return "bg-orange-100 text-orange-800 border-orange-200" case "upcoming": return "bg-gray-100 text-gray-800 border-gray-200" default: return "bg-gray-100 text-gray-800 border-gray-200" } } const getStatusText = (status: string) => { switch (status) { case "completed": return "已完成" case "active": return "進行中" case "judging": return "評審中" case "upcoming": return "即將開始" default: return status } } const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text) setSuccess("連結已複製到剪貼簿!") setTimeout(() => setSuccess(""), 3000) } // 获取筛选后的奖项 const getFilteredAwards = () => { let filteredAwards = [...awards] // 搜索功能 - 按应用名称、创作者或奖项名称搜索 if (awardSearchQuery.trim()) { const query = awardSearchQuery.toLowerCase().trim() filteredAwards = filteredAwards.filter((award) => { return ( award.appName?.toLowerCase().includes(query) || award.creator?.toLowerCase().includes(query) || award.awardName?.toLowerCase().includes(query) ) }) } // 年份筛选 if (awardYearFilter !== "all") { filteredAwards = filteredAwards.filter((award) => award.year === Number.parseInt(awardYearFilter)) } // 月份筛选 if (awardMonthFilter !== "all") { filteredAwards = filteredAwards.filter((award) => award.month === Number.parseInt(awardMonthFilter)) } // 奖项类型筛选 if (awardTypeFilter !== "all") { if (awardTypeFilter === "ranking") { filteredAwards = filteredAwards.filter((award) => award.rank > 0 && award.rank <= 3) } else if (awardTypeFilter === "popular") { filteredAwards = filteredAwards.filter((award) => award.awardType === "popular") } else { filteredAwards = filteredAwards.filter((award) => award.awardType === awardTypeFilter) } } // 竞赛类型筛选 if (awardCompetitionTypeFilter !== "all") { filteredAwards = filteredAwards.filter((award) => award.competitionType === awardCompetitionTypeFilter) } return filteredAwards.sort((a, b) => { // 按年份、月份、排名排序 if (a.year !== b.year) return b.year - a.year if (a.month !== b.month) return b.month - a.month if (a.rank !== b.rank) { if (a.rank === 0) return 1 if (b.rank === 0) return -1 return a.rank - b.rank } return 0 }) } const judgeScoringUrl = typeof window !== "undefined" ? `${window.location.origin}/judge-scoring` : "/judge-scoring" // Filter out proposal competitions from display const displayCompetitions = competitions.filter((competition) => competition.type !== "proposal") return (
{/* Success/Error Messages */} {success && ( {success} )} {error && ( {error} )} {/* Header */}

競賽管理

管理個人賽、團體賽、混合賽競賽活動

{currentCompetition && (
當前競賽:{currentCompetition.name} ({getCompetitionTypeText(currentCompetition.type)})
)}
{/* Stats Cards */}

總競賽數

{isLoadingDb ? ( ) : ( dbStats?.total || displayCompetitions.length )}

進行中

{isLoadingDb ? ( ) : ( dbStats?.active || displayCompetitions.filter((c) => c.status === "active").length )}

評審團

{judgeStats?.totalJudges || dbJudges.length || judges.length}

已頒獎項

{awards.length}

競賽列表 團隊管理 評審管理 評分管理 獎項管理 競賽列表 管理所有競賽活動 競賽名稱 類型 時間 狀態 參賽項目 評分進度 操作 {isLoadingDb ? (

載入競賽列表中...

) : (dbCompetitions.length > 0 ? dbCompetitions : displayCompetitions).length === 0 ? (

尚無競賽資料

點擊「創建競賽」按鈕開始建立第一個競賽

) : (dbCompetitions.length > 0 ? dbCompetitions : displayCompetitions).map((competition) => { const isCurrentCompetition = currentCompetition?.id === competition.id const scoringProgress = getScoringProgress(competition.id) const participantCount = getParticipantCount(competition) return (
{isCurrentCompetition && }

{competition.name}

{competition.description}

{getCompetitionTypeIcon(competition.type)} {getCompetitionTypeText(competition.type)}

{competition.year}年{competition.month}月

{formatDateRange( competition.start_date || competition.startDate, competition.end_date || competition.endDate )}

{getStatusText(competition.status)}
{getCompetitionTypeIcon(competition.type)} {participantCount} 個
{scoringProgress.completed}/{scoringProgress.total}

{scoringProgress.percentage}% 完成

handleViewCompetition(competition)}> 查看詳情 handleEditCompetition(competition)}> 編輯競賽 handleChangeStatus(competition)}> 修改狀態 handleManualScoring(competition)}> 手動評分 {!isCurrentCompetition && ( setCurrentCompetition(competition)}> 設為當前競賽 )} {isCurrentCompetition && ( setCurrentCompetition(null)}> 取消當前競賽 )} handleDeleteCompetition(competition)} className="text-red-600 focus:text-red-600" > 刪除競賽
) })}

團隊管理

{/* 搜索和篩選區域 */}
{/* 搜索框 */}
{ setTeamSearchTerm(e.target.value) setTeamCurrentPage(1) // 重置到第一頁 }} className="pl-10" />
{/* 部門篩選 */} {/* 清除篩選按鈕 */}
{/* 結果統計 */}
共找到 {(() => { const filtered = dbTeams.filter(team => { const matchesSearch = teamSearchTerm === "" || team.name.toLowerCase().includes(teamSearchTerm.toLowerCase()) || (team.leader_name && team.leader_name.toLowerCase().includes(teamSearchTerm.toLowerCase())) const matchesDepartment = teamDepartmentFilter === "all" || (teamDepartmentFilter === "其他" ? !["HQBU", "ITBU", "MBU1", "MBU2", "SBU", "研發部", "產品部", "技術部"].includes(team.department) : team.department === teamDepartmentFilter) return matchesSearch && matchesDepartment }) return filtered.length })()} 個團隊
{(() => { // 篩選邏輯 const filtered = dbTeams.filter(team => { const matchesSearch = teamSearchTerm === "" || team.name.toLowerCase().includes(teamSearchTerm.toLowerCase()) || (team.leader_name && team.leader_name.toLowerCase().includes(teamSearchTerm.toLowerCase())) const matchesDepartment = teamDepartmentFilter === "all" || (teamDepartmentFilter === "其他" ? !["HQBU", "ITBU", "MBU1", "MBU2", "SBU", "研發部", "產品部", "技術部"].includes(team.department) : team.department === teamDepartmentFilter) return matchesSearch && matchesDepartment }) // 分頁邏輯 const startIndex = (teamCurrentPage - 1) * teamsPerPage const endIndex = startIndex + teamsPerPage const paginatedTeams = filtered.slice(startIndex, endIndex) // 如果沒有找到任何團隊 if (filtered.length === 0) { return (

沒有找到團隊

{teamSearchTerm || teamDepartmentFilter !== "all" ? "請調整搜索條件或篩選條件" : "點擊「創建團隊」按鈕來添加第一個團隊"}

) } return paginatedTeams.map((team) => (

{team.name}

隊長:{team.leader_name || team.leader}
{team.department}
{team.contact_email}
{team.member_count || 0} 名成員
{team.submittedAppCount} 個應用

{team.description}

)) })()}
{/* 分頁組件 */} {(() => { const filtered = dbTeams.filter(team => { const matchesSearch = teamSearchTerm === "" || team.name.toLowerCase().includes(teamSearchTerm.toLowerCase()) || (team.leader_name && team.leader_name.toLowerCase().includes(teamSearchTerm.toLowerCase())) const matchesDepartment = teamDepartmentFilter === "all" || (teamDepartmentFilter === "其他" ? !["HQBU", "ITBU", "MBU1", "MBU2", "SBU", "研發部", "產品部", "技術部"].includes(team.department) : team.department === teamDepartmentFilter) return matchesSearch && matchesDepartment }) const totalPages = Math.ceil(filtered.length / teamsPerPage) // 如果當前頁面超出總頁數,重置到第一頁 if (teamCurrentPage > totalPages && totalPages > 0) { setTeamCurrentPage(1) } if (totalPages <= 1) return null return (
第 {teamCurrentPage} 頁,共 {totalPages} 頁
{Array.from({ length: Math.min(totalPages, 5) }, (_, i) => { let page; if (totalPages <= 5) { page = i + 1; } else if (teamCurrentPage <= 3) { page = i + 1; } else if (teamCurrentPage >= totalPages - 2) { page = totalPages - 4 + i; } else { page = teamCurrentPage - 2 + i; } return ( ) })}
) })()}

評審管理

{/* 搜索和篩選區域 */}
{/* 搜索框 */}
{ setJudgeSearchTerm(e.target.value) setJudgeCurrentPage(1) // 重置到第一頁 }} className="pl-10" />
{/* 部門篩選 */} {/* 專業領域篩選 */} {/* 狀態篩選 */} {/* 清除篩選按鈕 */}
{/* 結果統計 */}
共找到 {isLoadingJudges ? ( ) : (() => { const judgesToShow = dbJudges.length > 0 ? dbJudges : judges; const filtered = judgesToShow.filter(judge => { const matchesSearch = judgeSearchTerm === "" || judge.name.toLowerCase().includes(judgeSearchTerm.toLowerCase()) || judge.title.toLowerCase().includes(judgeSearchTerm.toLowerCase()) const matchesDepartment = judgeDepartmentFilter === "all" || (judgeDepartmentFilter === "其他" ? !["HQBU", "ITBU", "MBU1", "MBU2", "SBU", "研發部", "產品部", "技術部"].includes(judge.department) : judge.department === judgeDepartmentFilter) const matchesExpertise = judgeExpertiseFilter === "all" || judge.expertise.some(exp => exp.includes(judgeExpertiseFilter)) const matchesStatus = judgeStatusFilter === "all" || (judgeStatusFilter === "active" && judge.is_active === true) || (judgeStatusFilter === "inactive" && judge.is_active === false) return matchesSearch && matchesDepartment && matchesExpertise && matchesStatus }) return filtered.length })()} 位評審
{isLoadingJudges ? (

載入評審列表中...

) : (() => { // 使用資料庫數據或 context 數據 const judgesToShow = dbJudges.length > 0 ? dbJudges : judges; console.log('🔍 評審列表渲染 - dbJudges:', dbJudges.map(j => ({ id: j.id, name: j.name, is_active: j.is_active }))) console.log('🔍 評審列表渲染 - judges (context):', judges.map(j => ({ id: j.id, name: j.name, is_active: j.is_active }))) console.log('🔍 評審列表渲染 - judgesToShow:', judgesToShow.map(j => ({ id: j.id, name: j.name, is_active: j.is_active }))) // 篩選邏輯 const filtered = judgesToShow.filter(judge => { const matchesSearch = judgeSearchTerm === "" || judge.name.toLowerCase().includes(judgeSearchTerm.toLowerCase()) || judge.title.toLowerCase().includes(judgeSearchTerm.toLowerCase()) const matchesDepartment = judgeDepartmentFilter === "all" || (judgeDepartmentFilter === "其他" ? !["HQBU", "ITBU", "MBU1", "MBU2", "SBU", "研發部", "產品部", "技術部"].includes(judge.department) : judge.department === judgeDepartmentFilter) const matchesExpertise = judgeExpertiseFilter === "all" || judge.expertise.some(exp => exp.includes(judgeExpertiseFilter)) const matchesStatus = judgeStatusFilter === "all" || (judgeStatusFilter === "active" && judge.is_active === true) || (judgeStatusFilter === "inactive" && judge.is_active === false) return matchesSearch && matchesDepartment && matchesExpertise && matchesStatus }) // 分頁邏輯 const startIndex = (judgeCurrentPage - 1) * judgesPerPage const endIndex = startIndex + judgesPerPage const paginatedJudges = filtered.slice(startIndex, endIndex) // 如果沒有找到任何評審 if (filtered.length === 0) { return (

沒有找到評審

{judgeSearchTerm || judgeDepartmentFilter !== "all" || judgeExpertiseFilter !== "all" ? "請調整搜索條件或篩選條件" : "點擊「新增評審」按鈕來添加第一位評審"}

) } return paginatedJudges.map((judge) => (
{judge.name[0]}

{judge.name}

{judge.is_active === false && ( 已停用 )}

{judge.title}

{judge.department}

ID: {judge.id}

{judge.expertise.slice(0, 3).map((skill) => ( {skill} ))} {judge.expertise.length > 3 && ( +{judge.expertise.length - 3} )}
{judge.is_active !== false ? ( ) : ( )}
)) })()}
{/* 分頁組件 */} {(() => { // 使用與主列表相同的數據源 const judgesToShow = dbJudges.length > 0 ? dbJudges : judges; const filtered = judgesToShow.filter(judge => { const matchesSearch = judgeSearchTerm === "" || judge.name.toLowerCase().includes(judgeSearchTerm.toLowerCase()) || judge.title.toLowerCase().includes(judgeSearchTerm.toLowerCase()) const matchesDepartment = judgeDepartmentFilter === "all" || (judgeDepartmentFilter === "其他" ? !["HQBU", "ITBU", "MBU1", "MBU2", "SBU", "研發部", "產品部", "技術部"].includes(judge.department) : judge.department === judgeDepartmentFilter) const matchesExpertise = judgeExpertiseFilter === "all" || judge.expertise.some(exp => exp.includes(judgeExpertiseFilter)) const matchesStatus = judgeStatusFilter === "all" || (judgeStatusFilter === "active" && judge.is_active === true) || (judgeStatusFilter === "inactive" && judge.is_active === false) return matchesSearch && matchesDepartment && matchesExpertise && matchesStatus }) const totalPages = Math.ceil(filtered.length / judgesPerPage) // 如果當前頁面超出總頁數,重置到第一頁 if (judgeCurrentPage > totalPages && totalPages > 0) { setJudgeCurrentPage(1) } if (totalPages <= 1) return null return (
第 {judgeCurrentPage} 頁,共 {totalPages} 頁
{Array.from({ length: Math.min(totalPages, 5) }, (_, i) => { let page; if (totalPages <= 5) { page = i + 1; } else if (judgeCurrentPage <= 3) { page = i + 1; } else if (judgeCurrentPage >= totalPages - 2) { page = totalPages - 4 + i; } else { page = judgeCurrentPage - 2 + i; } return ( ) })}
) })()}

獎項管理

{/* 搜索和筛选控件 */} {awards.length > 0 && (
{/* 搜索栏 */}
{ setAwardSearchQuery(e.target.value) resetAwardPagination() }} className="pl-10 pr-10 w-full md:w-96" /> {awardSearchQuery && ( )}
{/* 筛选控件 */}
年份:
月份:
獎項類型:
競賽類型:
{/* 清除筛选按钮 */} {(awardSearchQuery || awardYearFilter !== "all" || awardMonthFilter !== "all" || awardTypeFilter !== "all" || awardCompetitionTypeFilter !== "all") && (
)}
{/* 统计信息 */}
{getFilteredAwards().length}
篩選結果
{getFilteredAwards().filter((a) => a.rank > 0 && a.rank <= 3).length}
前三名獎項
{getFilteredAwards().filter((a) => a.awardType === "popular").length}
人氣獎項
{new Set(getFilteredAwards().map((a) => `${a.year}-${a.month}`)).size}
競賽場次
)} {awards.length === 0 ? (

尚未頒發任何獎項

為競賽參賽者創建獎項,展示他們的成就

) : ( <> {(() => { const filteredAwards = getFilteredAwards() if (filteredAwards.length === 0) { return (
{awardSearchQuery ? ( ) : ( )}

{awardSearchQuery ? ( <>找不到包含「{awardSearchQuery}」的獎項 ) : ( <>暫無符合篩選條件的獎項 )}

{awardSearchQuery ? "嘗試使用其他關鍵字或調整篩選條件" : "請調整篩選條件查看其他獎項"}

{awardSearchQuery && ( )}
) } const startIndex = (awardCurrentPage - 1) * awardsPerPage const endIndex = startIndex + awardsPerPage const paginatedAwards = filteredAwards.slice(startIndex, endIndex) return (
{paginatedAwards.map((award) => (
{award.icon}
{/* 獎項基本資訊 */}
{award.awardName}

{award.appName || "團隊作品"}

by {award.creator}

{/* 評分顯示 */} {award.score > 0 && (
{award.score.toFixed(1)}
評審評分
)} {/* 獎項排名 */} {award.rank > 0 && ( 第 {award.rank} 名 )}
{/* 競賽資訊 */}
{award.year}年{award.month}月 {getCompetitionTypeText(award.competitionType)}
{/* 應用連結摘要 */} {(award as any).applicationLinks && (

應用連結

{(award as any).applicationLinks.production && (
)} {(award as any).applicationLinks.demo && (
)} {(award as any).applicationLinks.github && (
)}
)} {/* 相關文檔摘要 */} {(award as any).documents && (award as any).documents.length > 0 && (

相關文檔

{(award as any).documents.length} 個文檔 {(award as any).documents.map((doc: any) => doc.type).join(", ")}
)} {/* 得獎照片摘要 */} {(award as any).photos && (award as any).photos.length > 0 && (

得獎照片

{(award as any).photos.length} 張照片
{(award as any).photos.slice(0, 3).map((photo: any, index: number) => (
))} {(award as any).photos.length > 3 && ( +{(award as any).photos.length - 3} )}
)} {/* 獎項描述 */} {(award as any).description && (

{(award as any).description}

)} {/* 操作按鈕 */}
{(award as any).category || "innovation"}
))}
) })()} {/* 分頁組件 */} {(() => { const filteredAwards = getFilteredAwards() const totalPages = Math.ceil(filteredAwards.length / awardsPerPage) // 如果當前頁面超出總頁數,重置到第一頁 if (awardCurrentPage > totalPages && totalPages > 0) { setAwardCurrentPage(1) } if (totalPages <= 1) return null return (
第 {awardCurrentPage} 頁,共 {totalPages} 頁 • 篩選結果 {filteredAwards.length} 個獎項
{Array.from({ length: Math.min(totalPages, 5) }, (_, i) => { let page; if (totalPages <= 5) { page = i + 1; } else if (awardCurrentPage <= 3) { page = i + 1; } else if (awardCurrentPage >= totalPages - 2) { page = totalPages - 4 + i; } else { page = awardCurrentPage - 2 + i; } return ( ) })}
) })()} )}
{/* Create Competition Dialog - Enhanced for Mixed Competitions */} { setShowCreateCompetition(open) if (!open) { setCreateError("") setSelectedCompetitionForAction(null) // 清除編輯狀態 resetForm() // 重置表單 } }} > {selectedCompetitionForAction ? '編輯競賽' : '創建新競賽'} {selectedCompetitionForAction ? '修改競賽的基本資訊、類型和評比規則' : '設定競賽的基本資訊、類型和評比規則'}
{createError && ( {createError} )} {/* Basic Information */}

基本資訊

setNewCompetition({ ...newCompetition, name: e.target.value })} placeholder="輸入競賽名稱" />
setNewCompetition({ ...newCompetition, startDate: e.target.value })} />
setNewCompetition({ ...newCompetition, endDate: e.target.value })} />