diff --git a/app/api/admin/awards/route.ts b/app/api/admin/awards/route.ts index a5ea510..b9116a9 100644 --- a/app/api/admin/awards/route.ts +++ b/app/api/admin/awards/route.ts @@ -134,7 +134,7 @@ export async function GET(request: NextRequest) { awards = awards.filter(award => award.month === parseInt(month)); } - // 解析 JSON 欄位 + // 解析 JSON 欄位並統一欄位命名 const processedAwards = awards.map(award => { console.log('🔍 處理獎項:', { id: award.id, @@ -145,7 +145,15 @@ export async function GET(request: NextRequest) { return { ...award, - application_links: award.application_links ? JSON.parse(award.application_links) : null, + // 統一欄位命名:將下劃線命名轉換為駝峰命名 + competitionId: award.competition_id, + competitionName: (award as any).competition_name, + competitionType: (award as any).competition_type, + awardName: award.award_name, + awardType: award.award_type, + teamName: award.team_name, + appName: award.app_name, + applicationLinks: award.application_links ? JSON.parse(award.application_links) : null, documents: award.documents ? JSON.parse(award.documents) : [], photos: award.photos ? JSON.parse(award.photos) : [], }; diff --git a/app/api/admin/competitions/[id]/judges/route.ts b/app/api/admin/competitions/[id]/judges/route.ts index 06677b8..65b1996 100644 --- a/app/api/admin/competitions/[id]/judges/route.ts +++ b/app/api/admin/competitions/[id]/judges/route.ts @@ -3,7 +3,7 @@ // ===================================================== import { NextRequest, NextResponse } from 'next/server'; -import { AwardService } from '@/lib/services/database-service'; +import { CompetitionService } from '@/lib/services/database-service'; // 獲取競賽評審列表 export async function GET( @@ -22,7 +22,7 @@ export async function GET( console.log('🔍 獲取競賽評審:', competitionId); - const judges = await AwardService.getCompetitionJudges(competitionId); + const judges = await CompetitionService.getCompetitionJudges(competitionId); console.log('✅ 獲取到評審數量:', judges?.length || 0); diff --git a/app/api/competitions/[id]/judges/route.ts b/app/api/competitions/[id]/judges/route.ts index 097db5b..2a47273 100644 --- a/app/api/competitions/[id]/judges/route.ts +++ b/app/api/competitions/[id]/judges/route.ts @@ -9,10 +9,12 @@ import { CompetitionService } from '@/lib/services/database-service'; export async function GET(request: NextRequest, { params }: { params: { id: string } }) { try { const { id } = await params; + console.log('🔍 獲取競賽評審團 API 被調用,競賽ID:', id); // 獲取競賽信息 const competition = await CompetitionService.getCompetitionWithDetails(id); if (!competition) { + console.log('❌ 競賽不存在:', id); return NextResponse.json({ success: false, message: '競賽不存在', @@ -20,14 +22,22 @@ export async function GET(request: NextRequest, { params }: { params: { id: stri }, { status: 404 }); } + console.log('✅ 找到競賽:', competition.name); + // 獲取競賽的評審團 const judges = await CompetitionService.getCompetitionJudges(id); + console.log('📊 查詢到評審數量:', judges.length); + console.log('👥 評審詳細資料:', judges); - return NextResponse.json({ + const response = NextResponse.json({ success: true, message: '競賽評審團獲取成功', data: { - competition, + competition: { + id: competition.id, + name: competition.name, + type: competition.type + }, judges: judges.map(judge => ({ id: judge.id, name: judge.name, @@ -43,8 +53,15 @@ export async function GET(request: NextRequest, { params }: { params: { id: stri } }); + // 設置快取控制標頭,防止快取 + response.headers.set('Cache-Control', 'no-cache, no-store, must-revalidate'); + response.headers.set('Pragma', 'no-cache'); + response.headers.set('Expires', '0'); + + return response; + } catch (error) { - console.error('獲取競賽評審團失敗:', error); + console.error('❌ 獲取競賽評審團失敗:', error); return NextResponse.json({ success: false, message: '獲取競賽評審團失敗', diff --git a/app/page.tsx b/app/page.tsx index 797f6fd..613f69b 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -185,6 +185,13 @@ export default function AIShowcasePlatform() { ) const [showAwardDetail, setShowAwardDetail] = useState(false) const [selectedAward, setSelectedAward] = useState(null) + + // 添加頁面調試資訊 + console.log('🏠 主頁面渲染:', { + awardsCount: awards.length, + selectedAward: selectedAward ? selectedAward.id : null, + showAwardDetail + }); // 載入應用數據 const loadApps = async () => { @@ -418,6 +425,14 @@ export default function AIShowcasePlatform() { } const handleShowAwardDetail = (award: any) => { + console.log('🎯 handleShowAwardDetail 被調用:', { + award: award ? { + id: award.id, + competitionId: award.competitionId, + awardName: award.awardName, + hasCompetitionId: !!award.competitionId + } : null + }); setSelectedAward(award) setShowAwardDetail(true) } @@ -931,7 +946,17 @@ export default function AIShowcasePlatform() { {/* Award Detail Dialog */} {selectedAward && ( - + <> + {console.log('🎯 渲染 AwardDetailDialog:', { + selectedAward: selectedAward ? { + id: selectedAward.id, + competitionId: selectedAward.competitionId, + awardName: selectedAward.awardName + } : null, + showAwardDetail + })} + + )} ) : ( diff --git a/components/admin/competition-management.tsx b/components/admin/competition-management.tsx index 99e5c32..9f39609 100644 --- a/components/admin/competition-management.tsx +++ b/components/admin/competition-management.tsx @@ -102,6 +102,11 @@ export function CompetitionManagement() { // 可用用戶狀態 const [availableUsers, setAvailableUsers] = useState([]) const [isLoadingUsers, setIsLoadingUsers] = useState(false) + + // 獎項資料庫整合狀態 + const [dbAwards, setDbAwards] = useState([]) + const [isLoadingAwards, setIsLoadingAwards] = useState(false) + const [awardStats, setAwardStats] = useState(null) const [showCreateCompetition, setShowCreateCompetition] = useState(false) const [showAddJudge, setShowAddJudge] = useState(false) @@ -704,6 +709,7 @@ export function CompetitionManagement() { fetchTeams() fetchTeamStats() fetchAvailableApps() + loadAwardsData() }, []) // 當競賽列表載入完成後,載入每個競賽的評分進度 @@ -1980,6 +1986,15 @@ export function CompetitionManagement() { } const handleViewAward = async (award: any) => { + console.log('🎯 handleViewAward 被調用:', { + award: award ? { + id: award.id, + competitionId: award.competitionId, + awardName: award.award_name, + hasCompetitionId: !!award.competitionId + } : null + }); + setSelectedAward(award) setShowAwardDetail(true) @@ -1992,6 +2007,7 @@ export function CompetitionManagement() { if (data.success) { console.log('✅ 獲取到評審團:', data.data.length, '位'); + console.log('👥 評審團詳細資料:', data.data); setCompetitionJudges(data.data); } else { console.error('❌ 獲取評審團失敗:', data.message); @@ -2001,6 +2017,8 @@ export function CompetitionManagement() { console.error('❌ 載入評審團失敗:', error); setCompetitionJudges([]); } + } else { + console.log('❌ 獎項沒有 competitionId,無法載入評審團'); } } @@ -2080,6 +2098,41 @@ export function CompetitionManagement() { setShowManualScoring(true) } + // 載入獎項資料 + const loadAwardsData = async () => { + setIsLoadingAwards(true) + try { + console.log('🔍 開始載入獎項資料...') + const response = await fetch('/api/admin/awards') + const data = await response.json() + + if (data.success) { + console.log('✅ 獎項資料載入成功:', data.data.length, '個獎項') + setDbAwards(data.data) + + // 計算獎項統計 + const stats = { + total: data.data.length, + top3: data.data.filter((award: any) => award.rank && award.rank <= 3).length, + popular: data.data.filter((award: any) => award.award_type === 'popular').length, + competitions: [...new Set(data.data.map((award: any) => award.competition_id))].length + } + setAwardStats(stats) + console.log('📊 獎項統計:', stats) + } else { + console.error('❌ 獎項資料載入失敗:', data.message) + setDbAwards([]) + setAwardStats({ total: 0, top3: 0, popular: 0, competitions: 0 }) + } + } catch (error) { + console.error('❌ 載入獎項資料失敗:', error) + setDbAwards([]) + setAwardStats({ total: 0, top3: 0, popular: 0, competitions: 0 }) + } finally { + setIsLoadingAwards(false) + } + } + // 載入競賽相關數據用於評分 const loadCompetitionDataForScoring = async (competition: any) => { try { @@ -2735,7 +2788,8 @@ export function CompetitionManagement() { // 获取筛选后的奖项 const getFilteredAwards = () => { - let filteredAwards = [...awards] + // 使用資料庫中的獎項資料,如果沒有則使用 context 中的資料作為備用 + let filteredAwards = dbAwards.length > 0 ? [...dbAwards] : [...awards] // 搜索功能 - 按应用名称、创作者或奖项名称搜索 if (awardSearchQuery.trim()) { @@ -3888,24 +3942,26 @@ export function CompetitionManagement() { {/* 统计信息 */}
-
{getFilteredAwards().length}
+
+ {isLoadingAwards ? '...' : getFilteredAwards().length} +
篩選結果
- {getFilteredAwards().filter((a) => parseInt(a.rank) > 0 && parseInt(a.rank) <= 3).length} + {isLoadingAwards ? '...' : (awardStats?.top3 ?? getFilteredAwards().filter((a) => parseInt(a.rank) > 0 && parseInt(a.rank) <= 3).length)}
前三名獎項
- {getFilteredAwards().filter((a) => a.awardType === "popular").length} + {isLoadingAwards ? '...' : (awardStats?.popular ?? getFilteredAwards().filter((a) => a.award_type === "popular").length)}
人氣獎項
- {new Set(getFilteredAwards().map((a) => `${a.year}-${a.month}`)).size} + {isLoadingAwards ? '...' : (awardStats?.competitions ?? new Set(getFilteredAwards().map((a) => `${a.year}-${a.month}`)).size)}
競賽場次
@@ -7815,52 +7871,68 @@ export function CompetitionManagement() { - setNewAward({ ...newAward, awardName: e.target.value })} - placeholder="例如:最佳創新獎、金獎、銀獎" - /> + + {!newAward.customAwardTypeId && ( + setNewAward({ ...newAward, awardName: e.target.value })} + placeholder="例如:最佳創新獎、金獎、銀獎、人氣獎" + /> + )} + {competitionAwardTypes.length > 0 && ( +

+ 此競賽有 {competitionAwardTypes.length} 個自定義獎項名稱可選擇 +

+ )}
{/* 獎項類型 */}
- {competitionAwardTypes.length > 0 && ( -

- 此競賽有 {competitionAwardTypes.length} 個自定義獎項類型 -

- )}
{/* 獎項類別 */} diff --git a/components/competition/award-detail-dialog.tsx b/components/competition/award-detail-dialog.tsx index 2721b07..db98a0e 100644 --- a/components/competition/award-detail-dialog.tsx +++ b/components/competition/award-detail-dialog.tsx @@ -60,33 +60,95 @@ export function AwardDetailDialog({ open, onOpenChange, award }: AwardDetailDial const [currentPhotoIndex, setCurrentPhotoIndex] = useState(0) const [competitionJudges, setCompetitionJudges] = useState([]) + // 添加調試資訊 + console.log('🏆 AwardDetailDialog 渲染:', { + open, + award: award ? { + id: award.id, + competitionId: award.competitionId, + awardName: award.awardName, + hasCompetitionId: !!award.competitionId + } : null + }); + const competition = competitions.find((c) => c.id === award.competitionId) const judgeScores = getJudgeScores(award.id) const appData = getAppData(award.id) // 載入競賽評審團資訊 useEffect(() => { + console.log('🔍 useEffect 觸發:', { open, competitionId: award.competitionId, awardId: award.id }); + if (open && award.competitionId) { - const loadCompetitionJudges = async () => { + const loadCompetitionJudges = async (retryCount = 0) => { try { - console.log('🔍 載入競賽評審團:', award.competitionId); - const response = await fetch(`/api/competitions/${award.competitionId}/judges`); + console.log('🔍 載入競賽評審團:', award.competitionId, '重試次數:', retryCount); + console.log('🏆 獎項資料:', award); + + // 添加時間戳防止快取,並設置快取控制標頭 + const timestamp = Date.now(); + const apiUrl = `/api/competitions/${award.competitionId}/judges?t=${timestamp}`; + console.log('🌐 API URL:', apiUrl); + + const response = await fetch(apiUrl, { + method: 'GET', + headers: { + 'Cache-Control': 'no-cache, no-store, must-revalidate', + 'Pragma': 'no-cache', + 'Expires': '0' + } + }); + + console.log('📡 API 回應狀態:', response.status); + console.log('📡 API 回應標頭:', Object.fromEntries(response.headers.entries())); + + if (!response.ok) { + const errorText = await response.text(); + console.error('❌ API 錯誤回應:', errorText); + throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`); + } + const data = await response.json(); + console.log('📄 API 回應資料:', JSON.stringify(data, null, 2)); if (data.success && data.data && data.data.judges) { console.log('✅ 獲取到評審團:', data.data.judges.length, '位'); + console.log('👥 評審團詳細資料:', data.data.judges); setCompetitionJudges(data.data.judges); } else { - console.error('❌ 獲取評審團失敗:', data.message); - setCompetitionJudges([]); + console.error('❌ 獲取評審團失敗:', data.message || '未知錯誤'); + console.error('❌ 完整錯誤資料:', data); + + // 如果沒有評審資料且是第一次嘗試,重試一次 + if (retryCount === 0) { + console.log('🔄 重試載入評審團...'); + setTimeout(() => loadCompetitionJudges(1), 1000); + } else { + setCompetitionJudges([]); + } } } catch (error) { console.error('❌ 載入評審團失敗:', error); - setCompetitionJudges([]); + + // 如果是網路錯誤且是第一次嘗試,重試一次 + if (retryCount === 0) { + console.log('🔄 網路錯誤,重試載入評審團...'); + setTimeout(() => loadCompetitionJudges(1), 2000); + } else { + setCompetitionJudges([]); + } } }; + // 清空之前的評審資料,確保重新載入 + setCompetitionJudges([]); loadCompetitionJudges(); + } else { + console.log('❌ useEffect 條件不滿足:', { + open, + competitionId: award.competitionId, + hasCompetitionId: !!award.competitionId + }); } }, [open, award.competitionId]); diff --git a/components/competition/popularity-rankings.tsx b/components/competition/popularity-rankings.tsx index 5f5116b..1aafec4 100644 --- a/components/competition/popularity-rankings.tsx +++ b/components/competition/popularity-rankings.tsx @@ -287,7 +287,7 @@ export function PopularityRankings() { return ( @@ -454,7 +454,7 @@ export function PopularityRankings() { return ( @@ -487,8 +487,8 @@ export function PopularityRankings() {
團隊成員 ({team.members.length}人)
- {team.members.slice(0, 3).map((member: any) => ( -
+ {team.members.slice(0, 3).map((member: any, memberIndex: number) => ( +
{member.name[0]}
@@ -687,8 +687,8 @@ export function PopularityRankings() {
) : competitionJudges.length > 0 ? (
- {competitionJudges.map((judge) => ( -
+ {competitionJudges.map((judge, judgeIndex) => ( +
{judge.name[0]} @@ -697,8 +697,8 @@ export function PopularityRankings() {

{judge.name}

{judge.title}

- {judge.expertise.slice(0, 2).map((skill) => ( - + {judge.expertise.slice(0, 2).map((skill, skillIndex) => ( + {skill} ))} diff --git a/lib/services/database-service.ts b/lib/services/database-service.ts index 1e0632c..e1a02cd 100644 --- a/lib/services/database-service.ts +++ b/lib/services/database-service.ts @@ -1670,17 +1670,6 @@ export class CompetitionService extends DatabaseServiceBase { // 競賽關聯數據管理方法 // ===================================================== - // 獲取競賽的評審列表 - static async getCompetitionJudges(competitionId: string): Promise { - const sql = ` - SELECT j.*, cj.assigned_at - FROM competition_judges cj - JOIN judges j ON cj.judge_id = j.id - WHERE cj.competition_id = ? AND j.is_active = 1 - ORDER BY cj.assigned_at ASC - `; - return await DatabaseServiceBase.safeQuery(sql, [competitionId]); - } // 為競賽添加評審 static async addCompetitionJudges(competitionId: string, judgeIds: string[]): Promise { @@ -2080,6 +2069,63 @@ export class CompetitionService extends DatabaseServiceBase { rules }; } + + // 根據競賽ID獲取評審信息 + static async getCompetitionJudges(competitionId: string): Promise { + try { + console.log('🔍 查詢競賽評審:', competitionId); + + // 先檢查 competition_judges 表是否有資料 + const checkSql = `SELECT COUNT(*) as count FROM competition_judges WHERE competition_id = ?`; + const checkResult = await db.query(checkSql, [competitionId]); + console.log('📊 competition_judges 表中該競賽的記錄數:', checkResult[0]?.count || 0); + + // 檢查 judges 表是否有資料 + const judgesCountSql = `SELECT COUNT(*) as count FROM judges WHERE is_active = TRUE`; + const judgesCountResult = await db.query(judgesCountSql, []); + console.log('📊 judges 表中活躍評審數:', judgesCountResult[0]?.count || 0); + + // 檢查關聯查詢 + const joinCheckSql = ` + SELECT + cj.competition_id, + cj.judge_id, + j.id as judge_table_id, + j.name, + j.is_active + FROM competition_judges cj + LEFT JOIN judges j ON cj.judge_id = j.id + WHERE cj.competition_id = ? + `; + const joinResult = await db.query(joinCheckSql, [competitionId]); + console.log('🔗 關聯查詢結果:', joinResult); + + const sql = ` + SELECT + j.id, + j.name, + j.title, + j.department, + j.expertise, + j.avatar, + cj.assigned_at + FROM competition_judges cj + LEFT JOIN judges j ON cj.judge_id = j.id + WHERE cj.competition_id = ? AND j.is_active = TRUE + ORDER BY j.name + `; + console.log('📝 執行評審查詢SQL:', sql); + console.log('📝 查詢參數:', [competitionId]); + + const result = await db.query(sql, [competitionId]); + console.log('✅ 查詢評審結果:', result?.length || 0, '位評審'); + console.log('👥 評審詳細結果:', result); + return result; + } catch (error) { + console.error('❌ 查詢評審失敗:', error); + throw error; + } + } } // ===================================================== @@ -4569,33 +4615,6 @@ export class AwardService extends DatabaseServiceBase { return await db.queryOne(sql, [id]); } - // 根據競賽ID獲取評審信息 - static async getCompetitionJudges(competitionId: string): Promise { - try { - console.log('🔍 查詢競賽評審:', competitionId); - const sql = ` - SELECT - j.id, - j.name, - j.title, - j.department, - j.expertise, - j.avatar - FROM competition_judges cj - LEFT JOIN judges j ON cj.judge_id = j.id - WHERE cj.competition_id = ? AND j.is_active = TRUE - ORDER BY j.name - `; - console.log('📝 執行評審查詢SQL:', sql); - const result = await db.query(sql, [competitionId]); - console.log('✅ 查詢評審結果:', result?.length || 0, '位評審'); - return result; - } catch (error) { - console.error('❌ 查詢評審失敗:', error); - throw error; - } - } - // 更新獎項 static async updateAward(id: string, updates: Partial): Promise { const fields = Object.keys(updates).filter(key => @@ -4664,11 +4683,6 @@ export class AwardService extends DatabaseServiceBase { return await db.query(sql, [year]); } - // 根據競賽獲取獎項 - static async getAwardsByCompetition(competitionId: string): Promise { - const sql = 'SELECT * FROM awards WHERE competition_id = ? ORDER BY rank ASC'; - return await db.query(sql, [competitionId]); - } } // =====================================================