新增得獎更新、刪除的功能

This commit is contained in:
2025-09-29 22:52:53 +08:00
parent 57893128b2
commit ea6afb1675
6 changed files with 367 additions and 112 deletions

View File

@@ -71,6 +71,8 @@ export function CompetitionManagement() {
deleteJudge,
awards,
addAward,
updateAward,
deleteAward,
getAppDetailedScores,
judgeScores,
getAppJudgeScores,
@@ -1081,9 +1083,11 @@ export function CompetitionManagement() {
}
if (judgesData.success) {
console.log('✅ 載入評審成功:', judgesData.data?.length || 0, '位評審')
console.log('👥 評審詳細資料:', judgesData.data)
setCompetitionJudges(judgesData.data)
} else {
console.error('載入評審失敗:', judgesData.message)
console.error('載入評審失敗:', judgesData.message)
setCompetitionJudges([])
}
} catch (error) {
@@ -1870,7 +1874,7 @@ export function CompetitionManagement() {
// 根據獎項類型設定圖標
let icon = "🏆"
switch (newAward.awardType) {
switch (newAward.awardType as any) {
case "gold": icon = "🥇"; break;
case "silver": icon = "🥈"; break;
case "bronze": icon = "🥉"; break;
@@ -1908,9 +1912,14 @@ export function CompetitionManagement() {
console.log('📝 準備獎項數據:', awardData)
// 調用 API 創建獎項
const response = await fetch('/api/admin/awards', {
method: 'POST',
// 判斷是創建還是編輯模式
const isEditMode = selectedAward && selectedAward.id
const apiUrl = isEditMode ? `/api/admin/awards/${selectedAward.id}` : '/api/admin/awards'
const method = isEditMode ? 'PUT' : 'POST'
// 調用 API 創建或更新獎項
const response = await fetch(apiUrl, {
method: method,
headers: {
'Content-Type': 'application/json',
},
@@ -1920,36 +1929,38 @@ export function CompetitionManagement() {
const result = await response.json()
if (!response.ok) {
throw new Error(result.message || '創建獎項失敗')
throw new Error(result.message || (isEditMode ? '更新獎項失敗' : '創建獎項失敗'))
}
// 創建成功,添加到本地列表
const newAwardItem = {
id: result.data.id,
// 創建或更新成功,處理本地列表
const awardItem = {
id: result.data.id || selectedAward.id,
competitionId: newAward.competitionId,
competitionName: competition.name,
participantId: newAward.participantId,
participantType: newAward.participantType,
participantName: participantName,
creatorName: creatorName,
awardType: newAward.awardType,
appId: actualParticipantType === "individual" ? newAward.participantId : (participant?.app_id || null),
teamId: actualParticipantType === "team" ? newAward.participantId : null,
appName: actualParticipantType === "individual" ? participantName : (participant?.app_name || null),
teamName: actualParticipantType === "team" ? participantName : null,
creator: creatorName,
awardType: newAward.awardType as any,
awardName: newAward.awardName,
customAwardTypeId: newAward.customAwardTypeId,
score: newAward.score,
rank: newAward.rank,
category: newAward.category,
description: newAward.description,
judgeComments: newAward.judgeComments,
applicationLinks: newAward.applicationLinks,
documents: newAward.documents,
photos: newAward.photos,
year: new Date().getFullYear(),
month: new Date().getMonth() + 1,
icon: icon,
createdAt: new Date().toISOString(),
customAwardTypeId: newAward.customAwardTypeId,
competitionType: competition.type as any,
rank: newAward.rank,
category: newAward.category as any,
}
addAward(newAwardItem)
if (isEditMode) {
// 編輯模式:更新現有獎項
updateAward(awardItem)
} else {
// 創建模式:添加新獎項
const { id, ...awardWithoutId } = awardItem
addAward(awardWithoutId)
}
// 重置表單
setNewAward({
@@ -1974,8 +1985,9 @@ export function CompetitionManagement() {
})
setShowCreateAward(false)
setSuccess("獎項創建成功!")
setTimeout(() => setSuccess(""), 3000)
setSelectedAward(null)
setSuccess(isEditMode ? "獎項更新成功!" : "獎項創建成功!")
setTimeout(() => setSuccess(""), 3000)
} catch (error) {
console.error('創建獎項失敗:', error)
@@ -1991,7 +2003,10 @@ export function CompetitionManagement() {
id: award.id,
competitionId: award.competitionId,
awardName: award.award_name,
hasCompetitionId: !!award.competitionId
hasCompetitionId: !!award.competitionId,
photos: award.photos,
photosType: typeof award.photos,
photosLength: award.photos?.length
} : null
});
@@ -2022,7 +2037,8 @@ export function CompetitionManagement() {
}
}
const handleEditAward = (award: any) => {
const handleEditAward = async (award: any) => {
console.log('🎯 開始編輯獎項:', award)
setSelectedAward(award)
setNewAward({
competitionId: award.competitionId,
@@ -2044,6 +2060,16 @@ export function CompetitionManagement() {
judgeComments: (award as any).judgeComments || "",
photos: (award as any).photos || [],
})
// 載入對應競賽的評審和參賽者資料
if (award.competitionId) {
console.log('🔄 載入競賽資料競賽ID:', award.competitionId)
await loadCompetitionParticipants(award.competitionId)
console.log('✅ 載入完成,評審數量:', competitionJudges.length)
} else {
console.log('❌ 獎項沒有 competitionId')
}
setShowCreateAward(true)
}
@@ -2056,16 +2082,36 @@ export function CompetitionManagement() {
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)
try {
// 調用 API 刪除獎項
const response = await fetch(`/api/admin/awards/${awardToDelete.id}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
})
const result = await response.json()
if (!response.ok) {
throw new Error(result.message || '刪除獎項失敗')
}
// 刪除成功,從本地列表移除
deleteAward(awardToDelete.id)
setShowDeleteAwardConfirm(false)
setAwardToDelete(null)
setSuccess("獎項刪除成功!")
setTimeout(() => setSuccess(""), 3000)
} catch (error) {
console.error('刪除獎項失敗:', error)
setError(error instanceof Error ? error.message : '刪除獎項失敗')
} finally {
setIsLoading(false)
}
}
const handleManualScoring = async (competition: any) => {
@@ -4048,7 +4094,7 @@ export function CompetitionManagement() {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 min-h-[400px]">
{paginatedAwards.map((award: any) => (
<Card key={award.id} className="relative overflow-hidden hover:shadow-lg transition-shadow h-[280px] flex flex-col">
<Card key={award.id} className="relative overflow-hidden hover:shadow-lg transition-shadow flex flex-col">
<div className="absolute top-4 right-4 text-2xl">{award.icon}</div>
<CardContent className="p-4 flex-grow flex flex-col justify-between">
<div className="space-y-3 flex-grow">
@@ -9127,36 +9173,63 @@ export function CompetitionManagement() {
)}
{/* 得獎照片 */}
{(selectedAward as any).photos && (selectedAward as any).photos.length > 0 && (
<div className="space-y-3">
<h4 className="text-lg font-semibold text-gray-900"></h4>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{(selectedAward as any).photos.map((photo: any) => (
<div key={photo.id} className="space-y-2">
<div className="aspect-video bg-gray-100 rounded-lg border overflow-hidden">
{photo.url ? (
<img
src={photo.url}
alt={photo.caption || "得獎照片"}
className="w-full h-full object-cover"
onError={(e) => {
e.currentTarget.style.display = 'none';
e.currentTarget.nextElementSibling?.classList.remove('hidden');
}}
/>
) : null}
<div className="w-full h-full bg-gray-200 flex items-center justify-center text-2xl hidden">
🖼
{(() => {
const photos = (selectedAward as any)?.photos;
console.log('🖼️ 檢查照片資料:', {
hasPhotos: !!photos,
photosType: typeof photos,
photosLength: photos?.length,
photosData: photos
});
if (!photos || !Array.isArray(photos) || photos.length === 0) {
return null;
}
return (
<div className="space-y-3">
<h4 className="text-lg font-semibold text-gray-900"></h4>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{photos.map((photo: any, index: number) => {
console.log('📸 處理照片:', { index, photo });
return (
<div key={photo.id || photo.url || index} className="space-y-2">
<div className="aspect-video bg-gray-100 rounded-lg border overflow-hidden">
{photo.url ? (
<img
src={photo.url}
alt={photo.caption || photo.name || "得獎照片"}
className="w-full h-full object-cover"
onError={(e) => {
console.log('❌ 圖片載入失敗:', photo.url);
e.currentTarget.style.display = 'none';
e.currentTarget.nextElementSibling?.classList.remove('hidden');
}}
onLoad={() => {
console.log('✅ 圖片載入成功:', photo.url);
}}
/>
) : (
<div className="w-full h-full bg-gray-200 flex items-center justify-center text-2xl">
🖼
</div>
)}
<div className="w-full h-full bg-gray-200 flex items-center justify-center text-2xl hidden">
🖼
</div>
</div>
{(photo.caption || photo.name) && (
<p className="text-xs text-gray-600 text-center">
{photo.caption || photo.name}
</p>
)}
</div>
</div>
{photo.caption && (
<p className="text-xs text-gray-600 text-center">{photo.caption}</p>
)}
</div>
))}
);
})}
</div>
</div>
</div>
)}
);
})()}
<div className="flex justify-end space-x-3 pt-4 border-t">
<Button variant="outline" onClick={() => setShowAwardDetail(false)}>