修正評審評分清單失敗問題

This commit is contained in:
2025-09-21 01:30:26 +08:00
parent 049b53fa43
commit a36ab3c98d
17 changed files with 251 additions and 274 deletions

View File

@@ -26,10 +26,11 @@ interface ScoringRecord {
participantId: string
participantName: string
participantType: "individual" | "team"
teamName?: string
scores: Record<string, number>
totalScore: number
comments: string
submittedAt: string
submittedAt?: string
status: "completed" | "pending" | "draft"
}
@@ -198,8 +199,52 @@ export function ScoringManagement() {
return totalWeight > 0 ? totalScore / totalWeight : 0
}
// 生成所有評審和APP的組合
const generateAllScoringCombinations = () => {
if (!competitionJudges.length || !competitionParticipants.length) {
return []
}
const combinations: ScoringRecord[] = []
// 為每個評審和每個參賽者創建組合
competitionJudges.forEach(judge => {
competitionParticipants.forEach(participant => {
// 檢查是否已有評分記錄
const existingRecord = scoringRecords.find(record =>
record.judgeId === judge.id && record.participantId === participant.id
)
if (existingRecord) {
// 使用現有記錄
combinations.push(existingRecord)
} else {
// 創建新的待評分記錄
combinations.push({
id: `pending_${judge.id}_${participant.id}`,
judgeId: judge.id,
judgeName: judge.name,
participantId: participant.id,
participantName: participant.displayName || participant.name,
participantType: participant.type as "individual" | "team",
teamName: participant.teamName,
scores: {},
totalScore: 0,
comments: "",
status: "pending"
})
}
})
})
return combinations
}
const getFilteredRecords = () => {
let filtered = [...scoringRecords]
// 使用生成的組合而不是僅有的評分記錄
const allCombinations = generateAllScoringCombinations()
let filtered = [...allCombinations]
if (statusFilter !== "all") {
filtered = filtered.filter(record => record.status === statusFilter)
}
@@ -272,7 +317,13 @@ export function ScoringManagement() {
scores: initialScores,
comments: record.comments || '',
})
setShowEditScoring(true)
// 如果是待評分項目,顯示手動評分對話框;如果是已完成項目,顯示編輯對話框
if (record.status === "pending") {
setShowManualScoring(true)
} else {
setShowEditScoring(true)
}
}
const handleSubmitScore = async () => {
@@ -344,7 +395,36 @@ export function ScoringManagement() {
if (data.success) {
setSuccess(showEditScoring ? "評分更新成功!" : "評分提交成功!")
await loadScoringData() // 重新載入數據
// 更新本地評分記錄
const newRecord: ScoringRecord = {
id: data.data?.id || `new_${Date.now()}`,
judgeId: manualScoring.judgeId,
judgeName: competitionJudges.find(j => j.id === manualScoring.judgeId)?.name || '未知評審',
participantId: manualScoring.participantId,
participantName: competitionParticipants.find(p => p.id === manualScoring.participantId)?.displayName || competitionParticipants.find(p => p.id === manualScoring.participantId)?.name || '未知參賽者',
participantType: competitionParticipants.find(p => p.id === manualScoring.participantId)?.type as "individual" | "team" || "individual",
scores: apiScores,
totalScore: calculateTotalScore(apiScores, rules),
comments: manualScoring.comments.trim(),
status: "completed",
submittedAt: new Date().toISOString()
}
// 更新本地狀態
setScoringRecords(prev => {
const existingIndex = prev.findIndex(r => r.judgeId === newRecord.judgeId && r.participantId === newRecord.participantId)
if (existingIndex >= 0) {
// 更新現有記錄
const updated = [...prev]
updated[existingIndex] = newRecord
return updated
} else {
// 添加新記錄
return [...prev, newRecord]
}
})
setShowManualScoring(false)
setShowEditScoring(false)
setSelectedRecord(null)
@@ -463,7 +543,7 @@ export function ScoringManagement() {
name: app.name, // app 名稱
type: 'team',
teamName: team.name || '未知團隊', // 團隊名稱
displayName: `${team.name || '未知團隊'} - ${app.name}`, // 顯示名稱團隊名稱 - app名稱
displayName: app.name, // 顯示 app 名稱團隊名稱通過 teamName 屬性獲取
creator: team.members && team.members.find((m: any) => m.role === '隊長')?.name || '未知隊長',
teamId: team.id // 保存團隊 ID
})
@@ -583,13 +663,24 @@ export function ScoringManagement() {
setAppScoringDetails(null)
}
const progress = {
total: scoringStats.totalScores,
completed: scoringStats.completedScores,
pending: scoringStats.pendingScores,
percentage: scoringStats.completionRate
// 計算基於所有組合的統計數據
const calculateProgressStats = () => {
const allCombinations = generateAllScoringCombinations()
const total = allCombinations.length
const completed = allCombinations.filter(record => record.status === "completed").length
const pending = allCombinations.filter(record => record.status === "pending").length
const percentage = total > 0 ? Math.round((completed / total) * 100) : 0
return {
total,
completed,
pending,
percentage
}
}
const progress = calculateProgressStats()
// 顯示初始載入狀態
if (isInitialLoading) {
return (
@@ -677,7 +768,7 @@ export function ScoringManagement() {
<CardContent className="p-4">
<div className="text-center">
<p className="text-2xl font-bold text-blue-600">
{scoringSummary ? scoringSummary.overallStats.totalJudges : progress.completed}
{competitionJudges.length}
</p>
<p className="text-sm text-gray-600"></p>
</div>
@@ -687,7 +778,7 @@ export function ScoringManagement() {
<CardContent className="p-4">
<div className="text-center">
<p className="text-2xl font-bold text-green-600">
{scoringSummary ? scoringSummary.overallStats.totalApps : progress.pending}
{competitionParticipants.length}
</p>
<p className="text-sm text-gray-600">APP數</p>
</div>
@@ -697,7 +788,7 @@ export function ScoringManagement() {
<CardContent className="p-4">
<div className="text-center">
<p className="text-2xl font-bold text-orange-600">
{scoringSummary ? scoringSummary.overallStats.completedScores : progress.percentage}
{progress.completed}
</p>
<p className="text-sm text-gray-600"></p>
</div>
@@ -707,7 +798,7 @@ export function ScoringManagement() {
<CardContent className="p-4">
<div className="text-center">
<p className="text-2xl font-bold text-purple-600">
{scoringSummary ? `${scoringSummary.overallStats.overallCompletionRate}%` : progress.total}
{progress.percentage}%
</p>
<p className="text-sm text-gray-600"></p>
</div>
@@ -718,15 +809,10 @@ export function ScoringManagement() {
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span></span>
<span>
{scoringSummary ?
`${scoringSummary.overallStats.completedScores} / ${scoringSummary.overallStats.totalPossibleScores}` :
`${progress.completed} / ${progress.total}`
}
</span>
<span>{progress.completed} / {progress.total}</span>
</div>
<Progress
value={scoringSummary ? scoringSummary.overallStats.overallCompletionRate : progress.percentage}
value={progress.percentage}
className="h-2"
/>
</div>
@@ -876,7 +962,7 @@ export function ScoringManagement() {
onClick={() => {
const container = document.getElementById(`scroll-${judgeId}`)
if (container) {
container.scrollLeft -= 280 // 滑動一個卡片的寬度
container.scrollLeft -= 304 // 滑動一個卡片的寬度 (288px + 16px間距)
}
}}
className="absolute -left-6 top-1/2 transform -translate-y-1/2 z-10 bg-white border border-gray-300 rounded-full p-3 shadow-lg hover:shadow-xl transition-all duration-200 hover:bg-gray-50"
@@ -891,7 +977,7 @@ export function ScoringManagement() {
onClick={() => {
const container = document.getElementById(`scroll-${judgeId}`)
if (container) {
container.scrollLeft += 280 // 滑動一個卡片的寬度
container.scrollLeft += 304 // 滑動一個卡片的寬度 (288px + 16px間距)
}
}}
className="absolute -right-6 top-1/2 transform -translate-y-1/2 z-10 bg-white border border-gray-300 rounded-full p-3 shadow-lg hover:shadow-xl transition-all duration-200 hover:bg-gray-50"
@@ -906,26 +992,33 @@ export function ScoringManagement() {
style={{
scrollbarWidth: 'none',
msOverflowStyle: 'none',
maxWidth: 'calc(4 * 256px + 3 * 16px)' // 4個卡片 + 3個間距
maxWidth: 'calc(4 * 288px + 3 * 16px)' // 4個卡片 + 3個間距
}}
>
{records.map((record) => (
<div
key={record.id}
className="flex-shrink-0 w-64 bg-white border rounded-lg p-4 shadow-sm hover:shadow-md transition-all duration-200"
className="flex-shrink-0 w-72 bg-white border rounded-lg p-4 shadow-sm hover:shadow-md transition-all duration-200"
>
<div className="space-y-3">
{/* 項目標題和類型 */}
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<div className="flex items-start justify-between space-x-2">
<div className="flex items-start space-x-2 flex-1 min-w-0">
{record.participantType === "individual" ? (
<User className="w-4 h-4 text-blue-600" />
<User className="w-4 h-4 text-blue-600 flex-shrink-0 mt-0.5" />
) : (
<Users className="w-4 h-4 text-green-600" />
<Users className="w-4 h-4 text-green-600 flex-shrink-0 mt-0.5" />
)}
<span className="font-medium text-sm truncate">{record.participantName}</span>
<div className="min-w-0 flex-1">
<span className="font-medium text-sm leading-tight break-words block">
{record.participantType === "team" && record.teamName
? `${record.teamName} - ${record.participantName}`
: record.participantName
}
</span>
</div>
</div>
<Badge variant="outline" className="text-xs">
<Badge variant="outline" className="text-xs flex-shrink-0">
{record.participantType === "individual" ? "個人" : "團隊"}
</Badge>
</div>