修正獎勵專區評審團資料異常 bug

This commit is contained in:
2025-09-27 01:06:26 +08:00
parent 1aa8a06da1
commit 2597a07514
3 changed files with 160 additions and 65 deletions

View File

@@ -2143,11 +2143,17 @@ export function CompetitionManagement() {
if (teamsData.success && teamsData.data && teamsData.data.teams) { if (teamsData.success && teamsData.data && teamsData.data.teams) {
console.log('✅ 競賽團隊載入成功:', teamsData.data.teams.length, '個團隊') console.log('✅ 競賽團隊載入成功:', teamsData.data.teams.length, '個團隊')
updatedCompetition.teams = teamsData.data.teams // 確保團隊數據的唯一性避免重複的ID
// 同時更新dbTeams const uniqueTeams = teamsData.data.teams.filter((team: any, index: number, self: any[]) =>
self.findIndex(t => t.id === team.id) === index
)
updatedCompetition.teams = uniqueTeams
console.log('🔍 去重後的團隊數量:', uniqueTeams.length)
// 同時更新dbTeams確保不重複添加
setDbTeams(prev => { setDbTeams(prev => {
const existingIds = prev.map(t => t.id) const existingIds = prev.map(t => t.id)
const newTeams = teamsData.data.teams.filter((t: any) => !existingIds.includes(t.id)) const newTeams = uniqueTeams.filter((t: any) => !existingIds.includes(t.id))
return [...prev, ...newTeams] return [...prev, ...newTeams]
}) })
} else { } else {
@@ -3195,8 +3201,8 @@ export function CompetitionManagement() {
) )
} }
return paginatedTeams.map((team) => ( return paginatedTeams.map((team, index) => (
<Card key={team.id} className="relative h-fit"> <Card key={`${team.id}-${index}`} className="relative h-fit">
<CardContent className="p-4"> <CardContent className="p-4">
<div className="space-y-3"> <div className="space-y-3">
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
@@ -4067,7 +4073,7 @@ export function CompetitionManagement() {
if (links && links.production) { if (links && links.production) {
linkItems.push( linkItems.push(
<div key="production" className="flex items-center space-x-1"> <div key="production" className="flex items-center space-x-1">
<div className="w-2 h-2 bg-green-500 rounded-full" title="生產環境"></div> <div className="w-2 h-2 bg-green-500 rounded-full" title="APP 連結"></div>
<a <a
href={links.production} href={links.production}
target="_blank" target="_blank"
@@ -4075,7 +4081,7 @@ export function CompetitionManagement() {
className="text-xs text-blue-600 hover:text-blue-800 underline truncate max-w-24" className="text-xs text-blue-600 hover:text-blue-800 underline truncate max-w-24"
title={links.production} title={links.production}
> >
APP
</a> </a>
</div> </div>
); );
@@ -4538,7 +4544,7 @@ export function CompetitionManagement() {
<div className="col-span-4"> <div className="col-span-4">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Input <Input
value={rule.name || ""} value={rule.name ?? ""}
onChange={(e) => { onChange={(e) => {
const updatedRules = [...newCompetition.individualConfig.rules] const updatedRules = [...newCompetition.individualConfig.rules]
updatedRules[index] = { ...rule, name: e.target.value } updatedRules[index] = { ...rule, name: e.target.value }
@@ -4557,7 +4563,7 @@ export function CompetitionManagement() {
<div className="col-span-5"> <div className="col-span-5">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Input <Input
value={rule.description || ""} value={rule.description ?? ""}
onChange={(e) => { onChange={(e) => {
const updatedRules = [...newCompetition.individualConfig.rules] const updatedRules = [...newCompetition.individualConfig.rules]
updatedRules[index] = { ...rule, description: e.target.value } updatedRules[index] = { ...rule, description: e.target.value }
@@ -4579,7 +4585,7 @@ export function CompetitionManagement() {
type="number" type="number"
min="0" min="0"
max="100" max="100"
value={rule.weight || 0} value={rule.weight ?? 0}
onChange={(e) => { onChange={(e) => {
const updatedRules = [...newCompetition.individualConfig.rules] const updatedRules = [...newCompetition.individualConfig.rules]
updatedRules[index] = { ...rule, weight: Number.parseInt(e.target.value) || 0 } updatedRules[index] = { ...rule, weight: Number.parseInt(e.target.value) || 0 }
@@ -4672,7 +4678,7 @@ export function CompetitionManagement() {
<div className="col-span-1"> <div className="col-span-1">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Select <Select
value={awardType.icon || "🏆"} value={awardType.icon ?? "🏆"}
onValueChange={(value) => { onValueChange={(value) => {
const updatedAwardTypes = [...newCompetition.individualConfig.awardTypes] const updatedAwardTypes = [...newCompetition.individualConfig.awardTypes]
updatedAwardTypes[index] = { ...awardType, icon: value } updatedAwardTypes[index] = { ...awardType, icon: value }
@@ -4706,7 +4712,7 @@ export function CompetitionManagement() {
<div className="col-span-3"> <div className="col-span-3">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Input <Input
value={awardType.name || ""} value={awardType.name ?? ""}
onChange={(e) => { onChange={(e) => {
const updatedAwardTypes = [...newCompetition.individualConfig.awardTypes] const updatedAwardTypes = [...newCompetition.individualConfig.awardTypes]
updatedAwardTypes[index] = { ...awardType, name: e.target.value } updatedAwardTypes[index] = { ...awardType, name: e.target.value }
@@ -4725,7 +4731,7 @@ export function CompetitionManagement() {
<div className="col-span-5"> <div className="col-span-5">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Input <Input
value={awardType.description || ""} value={awardType.description ?? ""}
onChange={(e) => { onChange={(e) => {
const updatedAwardTypes = [...newCompetition.individualConfig.awardTypes] const updatedAwardTypes = [...newCompetition.individualConfig.awardTypes]
updatedAwardTypes[index] = { ...awardType, description: e.target.value } updatedAwardTypes[index] = { ...awardType, description: e.target.value }
@@ -4744,7 +4750,7 @@ export function CompetitionManagement() {
<div className="col-span-2"> <div className="col-span-2">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Select <Select
value={awardType.color || "text-yellow-600"} value={awardType.color ?? "text-yellow-600"}
onValueChange={(value) => { onValueChange={(value) => {
const updatedAwardTypes = [...newCompetition.individualConfig.awardTypes] const updatedAwardTypes = [...newCompetition.individualConfig.awardTypes]
updatedAwardTypes[index] = { ...awardType, color: value } updatedAwardTypes[index] = { ...awardType, color: value }
@@ -4950,7 +4956,7 @@ export function CompetitionManagement() {
<div className="col-span-4"> <div className="col-span-4">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Input <Input
value={rule.name || ""} value={rule.name ?? ""}
onChange={(e) => { onChange={(e) => {
const updatedRules = [...newCompetition.teamConfig.rules] const updatedRules = [...newCompetition.teamConfig.rules]
updatedRules[index] = { ...rule, name: e.target.value } updatedRules[index] = { ...rule, name: e.target.value }
@@ -4969,7 +4975,7 @@ export function CompetitionManagement() {
<div className="col-span-5"> <div className="col-span-5">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Input <Input
value={rule.description || ""} value={rule.description ?? ""}
onChange={(e) => { onChange={(e) => {
const updatedRules = [...newCompetition.teamConfig.rules] const updatedRules = [...newCompetition.teamConfig.rules]
updatedRules[index] = { ...rule, description: e.target.value } updatedRules[index] = { ...rule, description: e.target.value }
@@ -4991,7 +4997,7 @@ export function CompetitionManagement() {
type="number" type="number"
min="0" min="0"
max="100" max="100"
value={rule.weight || 0} value={rule.weight ?? 0}
onChange={(e) => { onChange={(e) => {
const updatedRules = [...newCompetition.teamConfig.rules] const updatedRules = [...newCompetition.teamConfig.rules]
updatedRules[index] = { ...rule, weight: Number.parseInt(e.target.value) || 0 } updatedRules[index] = { ...rule, weight: Number.parseInt(e.target.value) || 0 }
@@ -5084,7 +5090,7 @@ export function CompetitionManagement() {
<div className="col-span-1"> <div className="col-span-1">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Select <Select
value={awardType.icon || "🏆"} value={awardType.icon ?? "🏆"}
onValueChange={(value) => { onValueChange={(value) => {
const updatedAwardTypes = [...newCompetition.teamConfig.awardTypes] const updatedAwardTypes = [...newCompetition.teamConfig.awardTypes]
updatedAwardTypes[index] = { ...awardType, icon: value } updatedAwardTypes[index] = { ...awardType, icon: value }
@@ -5119,7 +5125,7 @@ export function CompetitionManagement() {
<div className="col-span-3"> <div className="col-span-3">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Input <Input
value={awardType.name || ""} value={awardType.name ?? ""}
onChange={(e) => { onChange={(e) => {
const updatedAwardTypes = [...newCompetition.teamConfig.awardTypes] const updatedAwardTypes = [...newCompetition.teamConfig.awardTypes]
updatedAwardTypes[index] = { ...awardType, name: e.target.value } updatedAwardTypes[index] = { ...awardType, name: e.target.value }
@@ -5138,7 +5144,7 @@ export function CompetitionManagement() {
<div className="col-span-5"> <div className="col-span-5">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Input <Input
value={awardType.description || ""} value={awardType.description ?? ""}
onChange={(e) => { onChange={(e) => {
const updatedAwardTypes = [...newCompetition.teamConfig.awardTypes] const updatedAwardTypes = [...newCompetition.teamConfig.awardTypes]
updatedAwardTypes[index] = { ...awardType, description: e.target.value } updatedAwardTypes[index] = { ...awardType, description: e.target.value }
@@ -5157,7 +5163,7 @@ export function CompetitionManagement() {
<div className="col-span-2"> <div className="col-span-2">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Select <Select
value={awardType.color || "text-yellow-600"} value={awardType.color ?? "text-yellow-600"}
onValueChange={(value) => { onValueChange={(value) => {
const updatedAwardTypes = [...newCompetition.teamConfig.awardTypes] const updatedAwardTypes = [...newCompetition.teamConfig.awardTypes]
updatedAwardTypes[index] = { ...awardType, color: value } updatedAwardTypes[index] = { ...awardType, color: value }
@@ -5350,7 +5356,7 @@ export function CompetitionManagement() {
<div className="col-span-4"> <div className="col-span-4">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Input <Input
value={rule.name || ""} value={rule.name ?? ""}
onChange={(e) => { onChange={(e) => {
const updatedRules = [...newCompetition.rules] const updatedRules = [...newCompetition.rules]
updatedRules[index] = { ...rule, name: e.target.value } updatedRules[index] = { ...rule, name: e.target.value }
@@ -5363,7 +5369,7 @@ export function CompetitionManagement() {
<div className="col-span-5"> <div className="col-span-5">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Input <Input
value={rule.description || ""} value={rule.description ?? ""}
onChange={(e) => { onChange={(e) => {
const updatedRules = [...newCompetition.rules] const updatedRules = [...newCompetition.rules]
updatedRules[index] = { ...rule, description: e.target.value } updatedRules[index] = { ...rule, description: e.target.value }
@@ -5379,7 +5385,7 @@ export function CompetitionManagement() {
type="number" type="number"
min="0" min="0"
max="100" max="100"
value={rule.weight || 0} value={rule.weight ?? 0}
onChange={(e) => { onChange={(e) => {
const updatedRules = [...newCompetition.rules] const updatedRules = [...newCompetition.rules]
updatedRules[index] = { ...rule, weight: Number.parseInt(e.target.value) || 0 } updatedRules[index] = { ...rule, weight: Number.parseInt(e.target.value) || 0 }
@@ -5468,7 +5474,7 @@ export function CompetitionManagement() {
<div className="col-span-1"> <div className="col-span-1">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Select <Select
value={awardType.icon || "🏆"} value={awardType.icon ?? "🏆"}
onValueChange={(value) => { onValueChange={(value) => {
const updatedAwardTypes = [...newCompetition.awardTypes] const updatedAwardTypes = [...newCompetition.awardTypes]
updatedAwardTypes[index] = { ...awardType, icon: value } updatedAwardTypes[index] = { ...awardType, icon: value }
@@ -5497,7 +5503,7 @@ export function CompetitionManagement() {
<div className="col-span-3"> <div className="col-span-3">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Input <Input
value={awardType.name || ""} value={awardType.name ?? ""}
onChange={(e) => { onChange={(e) => {
const updatedAwardTypes = [...newCompetition.awardTypes] const updatedAwardTypes = [...newCompetition.awardTypes]
updatedAwardTypes[index] = { ...awardType, name: e.target.value } updatedAwardTypes[index] = { ...awardType, name: e.target.value }
@@ -5510,7 +5516,7 @@ export function CompetitionManagement() {
<div className="col-span-5"> <div className="col-span-5">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Input <Input
value={awardType.description || ""} value={awardType.description ?? ""}
onChange={(e) => { onChange={(e) => {
const updatedAwardTypes = [...newCompetition.awardTypes] const updatedAwardTypes = [...newCompetition.awardTypes]
updatedAwardTypes[index] = { ...awardType, description: e.target.value } updatedAwardTypes[index] = { ...awardType, description: e.target.value }
@@ -5523,7 +5529,7 @@ export function CompetitionManagement() {
<div className="col-span-2"> <div className="col-span-2">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
<Select <Select
value={awardType.color || "text-yellow-600"} value={awardType.color ?? "text-yellow-600"}
onValueChange={(value) => { onValueChange={(value) => {
const updatedAwardTypes = [...newCompetition.awardTypes] const updatedAwardTypes = [...newCompetition.awardTypes]
updatedAwardTypes[index] = { ...awardType, color: value } updatedAwardTypes[index] = { ...awardType, color: value }
@@ -6175,8 +6181,12 @@ export function CompetitionManagement() {
</h4> </h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{(selectedCompetitionForAction.teams && selectedCompetitionForAction.teams.length > 0) ? {(selectedCompetitionForAction.teams && selectedCompetitionForAction.teams.length > 0) ?
selectedCompetitionForAction.teams.map((team: any) => ( selectedCompetitionForAction.teams
<div key={team.id} className="p-4 border rounded-lg bg-white space-y-2"> .filter((team: any, index: number, self: any[]) =>
self.findIndex(t => t.id === team.id) === index
)
.map((team: any, index: number) => (
<div key={`${team.id}-${index}`} className="p-4 border rounded-lg bg-white space-y-2">
<h5 className="font-medium text-gray-900">{team.name}</h5> <h5 className="font-medium text-gray-900">{team.name}</h5>
<p className="text-sm text-gray-600">{team.leader_name || team.leader_id || '未知'}</p> <p className="text-sm text-gray-600">{team.leader_name || team.leader_id || '未知'}</p>
<p className="text-sm text-gray-600">{team.department || '未知'}</p> <p className="text-sm text-gray-600">{team.department || '未知'}</p>
@@ -6485,12 +6495,16 @@ export function CompetitionManagement() {
(() => { (() => {
// 使用selectedCompetition.teams數據顯示為「團隊名 - APP名」格式 // 使用selectedCompetition.teams數據顯示為「團隊名 - APP名」格式
const teamsData = selectedCompetition?.teams || [] const teamsData = selectedCompetition?.teams || []
return teamsData.length > 0 ? teamsData.map((team: any) => { return teamsData.length > 0 ? teamsData
.filter((team: any, index: number, self: any[]) =>
self.findIndex(t => t.id === team.id) === index
)
.map((team: any, index: number) => {
// 檢查團隊是否有APP // 檢查團隊是否有APP
const teamApps = team.apps || [] const teamApps = team.apps || []
if (teamApps.length === 0) { if (teamApps.length === 0) {
return ( return (
<SelectItem key={team.id} value={team.id} disabled> <SelectItem key={`${team.id}-${index}`} value={team.id} disabled>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Users className="w-4 h-4 text-gray-400" /> <Users className="w-4 h-4 text-gray-400" />
<span className="text-gray-400">{team.name}</span> <span className="text-gray-400">{team.name}</span>
@@ -6503,7 +6517,7 @@ export function CompetitionManagement() {
// 顯示團隊和其第一個APP // 顯示團隊和其第一個APP
const firstApp = teamApps[0] const firstApp = teamApps[0]
return ( return (
<SelectItem key={team.id} value={team.id}> <SelectItem key={`${team.id}-${index}`} value={team.id}>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Users className="w-4 h-4 text-green-600" /> <Users className="w-4 h-4 text-green-600" />
<span>{team.name}</span> <span>{team.name}</span>
@@ -7813,7 +7827,7 @@ export function CompetitionManagement() {
<div className="space-y-2"> <div className="space-y-2">
<Label></Label> <Label></Label>
<Select <Select
value={newAward.customAwardTypeId || newAward.awardType} value={newAward.customAwardTypeId ?? newAward.awardType}
onValueChange={(value: any) => { onValueChange={(value: any) => {
// 檢查是否為自定義獎項類型 // 檢查是否為自定義獎項類型
const customAwardType = competitionAwardTypes.find(type => type.id === value) const customAwardType = competitionAwardTypes.find(type => type.id === value)
@@ -8095,8 +8109,12 @@ export function CompetitionManagement() {
} else if (shouldShowTeam) { } else if (shouldShowTeam) {
// 團體賽:只顯示團隊,不顯示 app // 團體賽:只顯示團隊,不顯示 app
return competitionTeams.length > 0 ? ( return competitionTeams.length > 0 ? (
competitionTeams.map((team) => ( competitionTeams
<SelectItem key={team.id} value={team.id}> .filter((team: any, index: number, self: any[]) =>
self.findIndex(t => t.id === team.id) === index
)
.map((team: any, index: number) => (
<SelectItem key={`${team.id}-${index}`} value={team.id}>
<div className="flex flex-col"> <div className="flex flex-col">
<span className="font-medium">{team.name}</span> <span className="font-medium">{team.name}</span>
<span className="text-xs text-gray-500"> <span className="text-xs text-gray-500">
@@ -8226,7 +8244,7 @@ export function CompetitionManagement() {
})} })}
placeholder="https://app.example.com" placeholder="https://app.example.com"
/> />
<p className="text-xs text-gray-500"></p> <p className="text-xs text-gray-500">APP </p>
</div> </div>
{/* 網站預覽內容 */} {/* 網站預覽內容 */}
@@ -8574,7 +8592,7 @@ export function CompetitionManagement() {
{/* 照片說明 */} {/* 照片說明 */}
<Input <Input
placeholder="輸入照片說明..." placeholder="輸入照片說明..."
value={photo.caption || ''} value={photo.caption ?? ''}
onChange={(e) => { onChange={(e) => {
const updatedPhotos = newAward.photos.map(p => const updatedPhotos = newAward.photos.map(p =>
p.id === photo.id ? { ...p, caption: e.target.value } : p p.id === photo.id ? { ...p, caption: e.target.value } : p
@@ -8925,7 +8943,7 @@ export function CompetitionManagement() {
<div key="production" className="p-3 bg-green-50 border border-green-200 rounded-lg"> <div key="production" className="p-3 bg-green-50 border border-green-200 rounded-lg">
<div className="flex items-center space-x-2 mb-2"> <div className="flex items-center space-x-2 mb-2">
<div className="w-3 h-3 bg-green-500 rounded-full"></div> <div className="w-3 h-3 bg-green-500 rounded-full"></div>
<span className="text-sm font-medium text-green-900"></span> <span className="text-sm font-medium text-green-900">APP </span>
</div> </div>
<a <a
href={links.production} href={links.production}

View File

@@ -1,6 +1,6 @@
"use client" "use client"
import { useState } from "react" import { useState, useEffect } from "react"
import { useCompetition } from "@/contexts/competition-context" import { useCompetition } from "@/contexts/competition-context"
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
@@ -58,11 +58,38 @@ export function AwardDetailDialog({ open, onOpenChange, award }: AwardDetailDial
const [activeTab, setActiveTab] = useState("overview") const [activeTab, setActiveTab] = useState("overview")
const [showPhotoGallery, setShowPhotoGallery] = useState(false) const [showPhotoGallery, setShowPhotoGallery] = useState(false)
const [currentPhotoIndex, setCurrentPhotoIndex] = useState(0) const [currentPhotoIndex, setCurrentPhotoIndex] = useState(0)
const [competitionJudges, setCompetitionJudges] = useState<any[]>([])
const competition = competitions.find((c) => c.id === award.competitionId) const competition = competitions.find((c) => c.id === award.competitionId)
const judgeScores = getJudgeScores(award.id) const judgeScores = getJudgeScores(award.id)
const appData = getAppData(award.id) const appData = getAppData(award.id)
// 載入競賽評審團資訊
useEffect(() => {
if (open && award.competitionId) {
const loadCompetitionJudges = async () => {
try {
console.log('🔍 載入競賽評審團:', award.competitionId);
const response = await fetch(`/api/competitions/${award.competitionId}/judges`);
const data = await response.json();
if (data.success && data.data && data.data.judges) {
console.log('✅ 獲取到評審團:', data.data.judges.length, '位');
setCompetitionJudges(data.data.judges);
} else {
console.error('❌ 獲取評審團失敗:', data.message);
setCompetitionJudges([]);
}
} catch (error) {
console.error('❌ 載入評審團失敗:', error);
setCompetitionJudges([]);
}
};
loadCompetitionJudges();
}
}, [open, award.competitionId]);
// Competition photos - empty for production // Competition photos - empty for production
const getCompetitionPhotos = () => { const getCompetitionPhotos = () => {
return [] return []
@@ -224,10 +251,20 @@ export function AwardDetailDialog({ open, onOpenChange, award }: AwardDetailDial
<strong></strong> <strong></strong>
{competition.description} {competition.description}
</p> </p>
<p> <p className="mb-2">
<strong></strong> <strong></strong>
{competition.startDate} ~ {competition.endDate} {competition.startDate} ~ {competition.endDate}
</p> </p>
<p>
<strong></strong>
{competitionJudges && competitionJudges.length > 0 ? (
<span className="text-green-700">
{competitionJudges.length}
</span>
) : (
<span className="text-gray-500"></span>
)}
</p>
</div> </div>
</div> </div>
)} )}
@@ -251,7 +288,7 @@ export function AwardDetailDialog({ open, onOpenChange, award }: AwardDetailDial
<ExternalLink className="w-5 h-5 text-green-600" /> <ExternalLink className="w-5 h-5 text-green-600" />
<div> <div>
<p className="font-medium text-green-800"></p> <p className="font-medium text-green-800"></p>
<p className="text-xs text-green-600"></p> <p className="text-xs text-green-600">APP </p>
</div> </div>
</div> </div>
<Button <Button
@@ -407,7 +444,25 @@ export function AwardDetailDialog({ open, onOpenChange, award }: AwardDetailDial
) )
const renderJudgePanel = () => { const renderJudgePanel = () => {
if (!competition) return null if (!competitionJudges || competitionJudges.length === 0) {
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Crown className="w-5 h-5 text-purple-500" />
<span></span>
</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<div className="text-center py-8 text-gray-500">
<Crown className="w-12 h-12 mx-auto mb-4 text-gray-300" />
<p></p>
</div>
</CardContent>
</Card>
)
}
return ( return (
<Card> <Card>
@@ -420,30 +475,25 @@ export function AwardDetailDialog({ open, onOpenChange, award }: AwardDetailDial
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{competition.judges.map((judgeId) => { {competitionJudges.map((judge) => (
const judge = judges.find((j) => j.id === judgeId) <div key={judge.id} className="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg">
if (!judge) return null <Avatar>
<AvatarImage src={judge.avatar} />
return ( <AvatarFallback className="bg-purple-100 text-purple-700">{judge.name[0]}</AvatarFallback>
<div key={judge.id} className="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg"> </Avatar>
<Avatar> <div className="flex-1">
<AvatarImage src={judge.avatar} /> <h4 className="font-medium">{judge.name}</h4>
<AvatarFallback className="bg-purple-100 text-purple-700">{judge.name[0]}</AvatarFallback> <p className="text-sm text-gray-600">{judge.title}</p>
</Avatar> <div className="flex flex-wrap gap-1 mt-1">
<div className="flex-1"> {judge.expertise && judge.expertise.slice(0, 2).map((skill) => (
<h4 className="font-medium">{judge.name}</h4> <Badge key={skill} variant="secondary" className="text-xs">
<p className="text-sm text-gray-600">{judge.title}</p> {skill}
<div className="flex flex-wrap gap-1 mt-1"> </Badge>
{judge.expertise.slice(0, 2).map((skill) => ( ))}
<Badge key={skill} variant="secondary" className="text-xs">
{skill}
</Badge>
))}
</div>
</div> </div>
</div> </div>
) </div>
})} ))}
</div> </div>
</CardContent> </CardContent>
</Card> </Card>

View File

@@ -0,0 +1,27 @@
-- =====================================================
-- 擴展 awards 表結構
-- 添加缺少的欄位以支持完整的獎項功能
-- =====================================================
-- 添加獎項描述欄位
ALTER TABLE `awards`
ADD COLUMN `description` TEXT NULL COMMENT '獎項描述' AFTER `category`;
-- 添加評審評語欄位
ALTER TABLE `awards`
ADD COLUMN `judge_comments` TEXT NULL COMMENT '評審評語' AFTER `description`;
-- 添加應用連結欄位JSON 格式)
ALTER TABLE `awards`
ADD COLUMN `application_links` JSON NULL COMMENT '應用連結' AFTER `judge_comments`;
-- 添加相關文檔欄位JSON 格式)
ALTER TABLE `awards`
ADD COLUMN `documents` JSON NULL COMMENT '相關文檔' AFTER `application_links`;
-- 添加得獎照片欄位JSON 格式)
ALTER TABLE `awards`
ADD COLUMN `photos` JSON NULL COMMENT '得獎照片' AFTER `documents`;
-- 驗證欄位是否添加成功
DESCRIBE `awards`;