"use client" import { 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 } 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 { Alert, AlertDescription } from "@/components/ui/alert" import { Progress } from "@/components/ui/progress" import { ScoringLinkDialog } from "./scoring-link-dialog" import { JudgeListDialog } from "./judge-list-dialog" import { Trophy, Plus, Edit, CheckCircle, AlertTriangle, ClipboardList, User, Users, Search, Loader2, BarChart3, ChevronLeft, ChevronRight, Link } from "lucide-react" interface ScoringRecord { id: string judgeId: string judgeName: string participantId: string participantName: string participantType: "individual" | "team" scores: Record totalScore: number comments: string submittedAt: string status: "completed" | "pending" | "draft" } const mockIndividualApps: any[] = [] const initialTeams: any[] = [] export function ScoringManagement() { const { competitions, judges, judgeScores, submitJudgeScore } = useCompetition() const [selectedCompetition, setSelectedCompetition] = useState(null) const [scoringRecords, setScoringRecords] = useState([]) const [showManualScoring, setShowManualScoring] = useState(false) const [showEditScoring, setShowEditScoring] = useState(false) const [selectedRecord, setSelectedRecord] = useState(null) const [manualScoring, setManualScoring] = useState({ judgeId: "", participantId: "", scores: {} as Record, comments: "" }) const [statusFilter, setStatusFilter] = useState<"all" | "completed" | "pending">("all") const [searchQuery, setSearchQuery] = useState("") const [isLoading, setIsLoading] = useState(false) const [success, setSuccess] = useState("") const [error, setError] = useState("") const [showScoringLink, setShowScoringLink] = useState(false) const [showJudgeList, setShowJudgeList] = useState(false) useEffect(() => { if (selectedCompetition) { loadScoringData() } }, [selectedCompetition]) const loadScoringData = () => { if (!selectedCompetition) return const participants = [ ...(selectedCompetition.participatingApps || []).map((appId: string) => { const app = mockIndividualApps.find(a => a.id === appId) return { id: appId, name: app?.name || `應用 ${appId}`, type: "individual" as const } }), ...(selectedCompetition.participatingTeams || []).map((teamId: string) => { const team = initialTeams.find(t => t.id === teamId) return { id: teamId, name: team?.name || `團隊 ${teamId}`, type: "team" as const } }) ] const records: ScoringRecord[] = [] participants.forEach(participant => { selectedCompetition.judges.forEach((judgeId: string) => { const judge = judges.find(j => j.id === judgeId) if (!judge) return const existingScore = judgeScores.find(score => score.judgeId === judgeId && score.appId === participant.id ) if (existingScore) { records.push({ id: `${judgeId}-${participant.id}`, judgeId, judgeName: judge.name, participantId: participant.id, participantName: participant.name, participantType: participant.type, scores: existingScore.scores, totalScore: calculateTotalScore(existingScore.scores, selectedCompetition.rules || []), comments: existingScore.comments, submittedAt: existingScore.submittedAt || new Date().toISOString(), status: "completed" as const, }) } else { // 初始化評分項目 const initialScores: Record = {} if (selectedCompetition.rules && selectedCompetition.rules.length > 0) { selectedCompetition.rules.forEach((rule: any) => { initialScores[rule.name] = 0 }) } else { // 預設評分項目 initialScores.innovation = 0 initialScores.technical = 0 initialScores.usability = 0 initialScores.presentation = 0 initialScores.impact = 0 } records.push({ id: `${judgeId}-${participant.id}`, judgeId, judgeName: judge.name, participantId: participant.id, participantName: participant.name, participantType: participant.type, scores: initialScores, totalScore: 0, comments: "", submittedAt: "", status: "pending" as const, }) } }) }) setScoringRecords(records) } const calculateTotalScore = (scores: Record, rules: any[]): number => { if (rules.length === 0) { const values = Object.values(scores) return values.length > 0 ? Math.round(values.reduce((a, b) => a + b, 0) / values.length) : 0 } let totalScore = 0 let totalWeight = 0 rules.forEach((rule: any) => { const score = scores[rule.name] || 0 const weight = rule.weight || 1 totalScore += score * weight totalWeight += weight }) return totalWeight > 0 ? Math.round(totalScore / totalWeight) : 0 } const getFilteredRecords = () => { let filtered = [...scoringRecords] if (statusFilter !== "all") { filtered = filtered.filter(record => record.status === statusFilter) } if (searchQuery.trim()) { const query = searchQuery.toLowerCase().trim() filtered = filtered.filter(record => record.judgeName.toLowerCase().includes(query) || record.participantName.toLowerCase().includes(query) ) } return filtered } const handleManualScoring = () => { // 根據競賽規則初始化評分項目 const initialScores: Record = {} if (selectedCompetition?.rules && selectedCompetition.rules.length > 0) { selectedCompetition.rules.forEach((rule: any) => { initialScores[rule.name] = 0 }) } else { // 預設評分項目 initialScores.innovation = 0 initialScores.technical = 0 initialScores.usability = 0 initialScores.presentation = 0 initialScores.impact = 0 } setManualScoring({ judgeId: "", participantId: "", scores: initialScores, comments: "" }) setShowManualScoring(true) } const handleEditScoring = (record: ScoringRecord) => { setSelectedRecord(record) setManualScoring({ judgeId: record.judgeId, participantId: record.participantId, scores: { ...record.scores }, comments: record.comments, }) setShowEditScoring(true) } const handleSubmitScore = async () => { setError("") if (!manualScoring.judgeId || !manualScoring.participantId) { setError("請選擇評審和參賽項目") return } // 檢查所有評分項目是否都已評分 const scoringRules = selectedCompetition?.rules || [] const defaultRules = [ { name: "創新性" }, { name: "技術性" }, { name: "實用性" }, { name: "展示效果" }, { name: "影響力" } ] const rules = scoringRules.length > 0 ? scoringRules : defaultRules const hasAllScores = rules.every((rule: any) => manualScoring.scores[rule.name] && manualScoring.scores[rule.name] > 0 ) if (!hasAllScores) { setError("請為所有評分項目打分") return } if (!manualScoring.comments.trim()) { setError("請填寫評審意見") return } setIsLoading(true) try { await submitJudgeScore({ judgeId: manualScoring.judgeId, appId: manualScoring.participantId, scores: manualScoring.scores, comments: manualScoring.comments.trim(), }) setSuccess(showEditScoring ? "評分更新成功!" : "評分提交成功!") loadScoringData() setShowManualScoring(false) setShowEditScoring(false) setSelectedRecord(null) } catch (err) { setError("評分提交失敗,請重試") } finally { setIsLoading(false) setTimeout(() => setSuccess(""), 3000) } } const getStatusBadge = (status: string) => { switch (status) { case "completed": return 已完成 case "pending": return 待評分 default: return {status} } } const getScoringProgress = () => { const total = scoringRecords.length const completed = scoringRecords.filter(r => r.status === "completed").length const pending = scoringRecords.filter(r => r.status === "pending").length const percentage = total > 0 ? Math.round((completed / total) * 100) : 0 return { total, completed, pending, percentage } } const progress = getScoringProgress() return (
{success && ( {success} )} {error && ( {error} )} 選擇競賽 選擇要管理的競賽評分 {selectedCompetition && ( <> {selectedCompetition.name} - 評分概覽 {selectedCompetition.type === "individual" ? "個人賽" : selectedCompetition.type === "team" ? "團體賽" : "混合賽"} 查看當前競賽的評分進度和詳情

{progress.completed}

已完成評分

{progress.pending}

待評分

{progress.percentage}%

完成度

{progress.total}

總評分項目

評分進度 {progress.completed} / {progress.total}
評分管理
狀態:
setSearchQuery(e.target.value)} className="pl-10" />
{(() => { // 按評審分組 const groupedByJudge = getFilteredRecords().reduce((groups, record) => { const judgeName = record.judgeName if (!groups[judgeName]) { groups[judgeName] = [] } groups[judgeName].push(record) return groups }, {} as Record) return Object.entries(groupedByJudge).map(([judgeName, records]) => { const completedCount = records.filter(r => r.status === "completed").length const totalCount = records.length const progressPercentage = totalCount > 0 ? Math.round((completedCount / totalCount) * 100) : 0 return (
{judgeName.charAt(0)}

{judgeName}

評分進度:{completedCount} / {totalCount} 項

{progressPercentage}%
完成度
{progressPercentage}%
{/* 左滑動箭頭 */} {records.length > 4 && ( )} {/* 右滑動箭頭 */} {records.length > 4 && ( )}
{records.map((record) => (
{/* 項目標題和類型 */}
{record.participantType === "individual" ? ( ) : ( )} {record.participantName}
{record.participantType === "individual" ? "個人" : "團隊"}
{/* 評分狀態 */}
{record.totalScore} / 10
{getStatusBadge(record.status)} {record.submittedAt && ( {new Date(record.submittedAt).toLocaleDateString()} )}
{/* 操作按鈕 */}
))}
) }) })()}
)} { if (!open) { setShowManualScoring(false) setShowEditScoring(false) setSelectedRecord(null) setManualScoring({ judgeId: "", participantId: "", scores: {} as Record, comments: "" }) } }}> {showEditScoring ? "編輯評分" : "手動輸入評分"} {showEditScoring ? "修改現有評分記錄" : "為參賽者手動輸入評分"}
{/* 動態評分項目 */}

評分項目

{(() => { const scoringRules = selectedCompetition?.rules || [] const defaultRules = [ { name: "創新性", description: "創新程度和獨特性", weight: 25 }, { name: "技術性", description: "技術實現的複雜度和品質", weight: 30 }, { name: "實用性", description: "實際應用價值和用戶體驗", weight: 20 }, { name: "展示效果", description: "展示的清晰度和吸引力", weight: 15 }, { name: "影響力", description: "對行業或社會的潛在影響", weight: 10 } ] const rules = scoringRules.length > 0 ? scoringRules : defaultRules return rules.map((rule: any, index: number) => (

{rule.description}

{rule.weight && (

權重:{rule.weight}%

)}
{manualScoring.scores[rule.name] || 0} / 10
{/* 評分按鈕 */}
{Array.from({ length: 10 }, (_, i) => i + 1).map((score) => ( ))}
)) })()}
{/* 總分顯示 */}
總分

根據權重計算的綜合評分

{calculateTotalScore(manualScoring.scores, selectedCompetition?.rules || [])} / 10