"use client" import { useState } 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, 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 [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") const [awardMonthFilter, setAwardMonthFilter] = useState("all") const [awardTypeFilter, setAwardTypeFilter] = useState("all") const [awardCompetitionTypeFilter, setAwardCompetitionTypeFilter] = useState("all") // 当筛选条件改变时重置分页 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: "", department: "HQBU", contactEmail: "", leaderPhone: "", description: "", members: [] as Array<{ id: string; name: string; department: string; role: string }>, apps: [] as string[], appLinks: [] as string[], submittedAppCount: 0, }) const [newMember, setNewMember] = useState({ name: "", 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 [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 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 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: [], appLinks: [], 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) await new Promise((resolve) => setTimeout(resolve, 1000)) if (selectedCompetitionForAction) { // 編輯模式 - 更新現有競賽 updateCompetition(selectedCompetitionForAction.id, newCompetition) setSuccess("競賽更新成功!") } else { // 創建模式 - 新增競賽 const competitionWithId = { ...newCompetition, id: `c${Date.now()}`, createdAt: new Date().toISOString(), } addCompetition(competitionWithId) setSuccess("競賽創建成功!") } 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) await new Promise((resolve) => setTimeout(resolve, 1000)) if (selectedTeam) { // 編輯模式 - 更新現有團隊 const updatedTeam = { ...selectedTeam, ...newTeam, memberCount: newTeam.members.length, submittedAppCount: newTeam.apps.length, } const updatedTeams = teams.map(team => team.id === selectedTeam.id ? updatedTeam : team ) setTeams(updatedTeams) setSuccess("團隊更新成功!") } else { // 創建模式 - 新增團隊 const team = { id: `t${Date.now()}`, ...newTeam, memberCount: newTeam.members.length, submissionDate: new Date().toISOString().split("T")[0], submittedAppCount: newTeam.apps.length, } setTeams([...teams, team]) setSuccess("團隊創建成功!") } setShowCreateTeam(false) setSelectedTeam(null) resetTeamForm() setIsLoading(false) setTimeout(() => setSuccess(""), 3000) } const handleEditTeam = (team: any) => { setSelectedTeam(team) setNewTeam({ name: team.name, leader: team.leader, department: team.department, contactEmail: team.contactEmail, leaderPhone: team.leaderPhone || "", description: team.description, members: [...team.members], apps: [...team.apps], appLinks: [...team.appLinks], submittedAppCount: team.submittedAppCount, }) setShowCreateTeam(true) // 使用創建團隊對話框 } const handleDeleteTeam = (team: any) => { setTeamToDelete(team) setShowDeleteTeamConfirm(true) } const handleConfirmDeleteTeam = async () => { if (!teamToDelete) return setIsLoading(true) await new Promise((resolve) => setTimeout(resolve, 500)) setTeams(teams.filter((team) => team.id !== teamToDelete.id)) setShowDeleteTeamConfirm(false) setTeamToDelete(null) setSuccess("團隊刪除成功!") setIsLoading(false) setTimeout(() => setSuccess(""), 3000) } const handleAddMember = () => { if (!newMember.name.trim()) { setCreateError("請輸入成員姓名") return } const member = { id: `m${Date.now()}`, name: newMember.name, department: newMember.department, role: newMember.role, } setNewTeam({ ...newTeam, members: [...newTeam.members, member], }) setNewMember({ name: "", department: "HQBU", role: "成員", }) setCreateError("") } const handleRemoveMember = (memberId: string) => { setNewTeam({ ...newTeam, members: newTeam.members.filter((m) => m.id !== memberId), }) } const handleAddApp = () => { if (!newApp.name.trim()) { setCreateError("請輸入應用名稱") return } setNewTeam({ ...newTeam, apps: [...newTeam.apps, newApp.name], appLinks: [...newTeam.appLinks, newApp.link], }) setNewApp({ name: "", link: "", }) setCreateError("") } const handleRemoveApp = (index: number) => { const newApps = [...newTeam.apps] const newAppLinks = [...newTeam.appLinks] newApps.splice(index, 1) newAppLinks.splice(index, 1) setNewTeam({ ...newTeam, apps: newApps, appLinks: newAppLinks, }) } const handleAddJudge = async () => { setError("") if (!newJudge.name || !newJudge.title || !newJudge.department) { setError("請填寫所有必填欄位") return } setIsLoading(true) await new Promise((resolve) => setTimeout(resolve, 1000)) if (selectedJudge) { // 編輯模式 - 更新現有評審 updateJudge(selectedJudge.id, { ...newJudge, expertise: newJudge.expertise .split(",") .map((s) => s.trim()) .filter(Boolean), }) setSuccess("評審更新成功!") } else { // 新增模式 - 新增評審 addJudge({ ...newJudge, expertise: newJudge.expertise .split(",") .map((s) => s.trim()) .filter(Boolean), }) setSuccess("評審新增成功!") } 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 handleDeleteJudge = (judge: any) => { setSelectedJudge(judge) setShowDeleteJudgeConfirm(true) } const confirmDeleteJudge = async () => { if (!selectedJudge) return setIsLoading(true) await new Promise((resolve) => setTimeout(resolve, 500)) deleteJudge(selectedJudge.id) setShowDeleteJudgeConfirm(false) setSelectedJudge(null) setSuccess("評審刪除成功!") setIsLoading(false) setTimeout(() => setSuccess(""), 3000) } 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 = 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 getParticipantCount = (competition: any) => { 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 */}

總競賽數

{displayCompetitions.length}

進行中

{displayCompetitions.filter((c) => c.status === "active").length}

評審團

{judges.length}

已頒獎項

{awards.length}

競賽列表 團隊管理 評審管理 評分管理 獎項管理 競賽列表 管理所有競賽活動 競賽名稱 類型 時間 狀態 參賽項目 評分進度 操作 {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}月

{competition.startDate} ~ {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 = teams.filter(team => { const matchesSearch = teamSearchTerm === "" || team.name.toLowerCase().includes(teamSearchTerm.toLowerCase()) || team.leader.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 = teams.filter(team => { const matchesSearch = teamSearchTerm === "" || team.name.toLowerCase().includes(teamSearchTerm.toLowerCase()) || team.leader.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}
{team.department}
{team.contactEmail}
{team.memberCount} 名成員
{team.submittedAppCount} 個應用

{team.description}

)) })()}
{/* 分頁組件 */} {(() => { const filtered = teams.filter(team => { const matchesSearch = teamSearchTerm === "" || team.name.toLowerCase().includes(teamSearchTerm.toLowerCase()) || team.leader.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" />
{/* 部門篩選 */} {/* 專業領域篩選 */} {/* 清除篩選按鈕 */}
{/* 結果統計 */}
共找到 {(() => { const filtered = judges.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)) return matchesSearch && matchesDepartment && matchesExpertise }) return filtered.length })()} 位評審
{(() => { // 篩選邏輯 const filtered = judges.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)) return matchesSearch && matchesDepartment && matchesExpertise }) // 分頁邏輯 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.title}

{judge.department}

ID: {judge.id}

{judge.expertise.slice(0, 3).map((skill) => ( {skill} ))} {judge.expertise.length > 3 && ( +{judge.expertise.length - 3} )}
)) })()}
{/* 分頁組件 */} {(() => { const filtered = judges.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)) return matchesSearch && matchesDepartment && matchesExpertise }) 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 })} />