From a36ab3c98de07fa39f6f189caabbef2421b87067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B3=E4=BD=A9=E5=BA=AD?= Date: Sun, 21 Sep 2025 01:30:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E8=A9=95=E5=AF=A9=E8=A9=95?= =?UTF-8?q?=E5=88=86=E6=B8=85=E5=96=AE=E5=A4=B1=E6=95=97=E5=95=8F=E9=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/admin/apps/available/route.ts | 56 +---- app/api/test-db/route.ts | 5 - app/debug-scoring/page.tsx | 1 - app/judge-scoring/page.tsx | 11 +- app/page.tsx | 2 - app/test-manual-scoring/page.tsx | 11 - components/admin/competition-management.tsx | 43 ---- components/admin/scoring-link-dialog.tsx | 8 +- components/admin/scoring-management.tsx | 155 +++++++++++--- components/admin/team-management.tsx | 4 - components/app-detail-dialog.tsx | 9 - .../competition/competition-detail-dialog.tsx | 1 - .../competition/popularity-rankings.tsx | 3 - components/favorites-page.tsx | 5 - components/reviews/review-system.tsx | 1 - contexts/competition-context.tsx | 18 -- lib/services/database-service.ts | 192 +++++++++++------- 17 files changed, 251 insertions(+), 274 deletions(-) diff --git a/app/api/admin/apps/available/route.ts b/app/api/admin/apps/available/route.ts index 0fc2c50..5b8053a 100644 --- a/app/api/admin/apps/available/route.ts +++ b/app/api/admin/apps/available/route.ts @@ -7,51 +7,10 @@ import { db } from '@/lib/database'; export async function GET(request: NextRequest) { try { - console.log('🚀 ========== 應用 API 開始執行 =========='); const { searchParams } = new URL(request.url); const teamId = searchParams.get('teamId'); - console.log('🔍 獲取可用應用列表, teamId:', teamId); - console.log('🔍 請求 URL:', request.url); - - // 先檢查所有應用 - console.log('📊 開始檢查數據庫...'); - const allAppsSql = `SELECT COUNT(*) as count FROM apps`; - const allAppsResult = await db.query(allAppsSql); - console.log('📊 數據庫中應用總數:', allAppsResult[0].count); - - // 檢查活躍應用 - const activeAppsSql = `SELECT COUNT(*) as count FROM apps WHERE is_active = TRUE`; - const activeAppsResult = await db.query(activeAppsSql); - console.log('✅ 活躍應用數量 (is_active = TRUE):', activeAppsResult[0].count); - - // 檢查所有應用的 is_active 值 - const allAppsWithStatusSql = `SELECT id, name, is_active, team_id FROM apps LIMIT 5`; - const allAppsWithStatusResult = await db.query(allAppsWithStatusSql); - console.log('📋 前5個應用的狀態:', allAppsWithStatusResult); - - // 檢查是否有 is_active = 1 的應用 - const activeAppsWith1Sql = `SELECT COUNT(*) as count FROM apps WHERE is_active = 1`; - const activeAppsWith1Result = await db.query(activeAppsWith1Sql); - console.log('✅ is_active = 1 的應用數量:', activeAppsWith1Result[0].count); - - // 檢查是否有 is_active = '1' 的應用(字符串) - const activeAppsWithStringSql = `SELECT COUNT(*) as count FROM apps WHERE is_active = '1'`; - const activeAppsWithStringResult = await db.query(activeAppsWithStringSql); - console.log('✅ is_active = "1" 的應用數量:', activeAppsWithStringResult[0].count); - - // 檢查沒有團隊的應用 - const noTeamAppsSql = `SELECT COUNT(*) as count FROM apps WHERE is_active = 1 AND team_id IS NULL`; - const noTeamAppsResult = await db.query(noTeamAppsSql); - console.log('🔓 沒有團隊的應用數量:', noTeamAppsResult[0].count); - - // 檢查屬於其他團隊的應用 - const otherTeamAppsSql = `SELECT COUNT(*) as count FROM apps WHERE is_active = 1 AND team_id IS NOT NULL AND team_id != ?`; - const otherTeamAppsResult = await db.query(otherTeamAppsSql, [teamId || '']); - console.log('🔓 屬於其他團隊的應用數量:', otherTeamAppsResult[0].count); - // 獲取所有活躍的應用,編輯團隊時顯示所有應用(包括已綁定的) - // 使用 is_active = 1 因為數據庫中存儲的是數字 1 // 與 users 表 JOIN 獲取創建者姓名 let sql = ` SELECT a.id, a.name, a.description, a.category, a.type, a.icon, a.icon_color, a.app_url, @@ -65,48 +24,36 @@ export async function GET(request: NextRequest) { const params: any[] = []; - console.log('📝 執行的 SQL:', sql); - console.log('📝 參數:', params); - const apps = await db.query(sql, params); - console.log('📊 查詢結果:', apps.length, '個應用'); // 如果沒有結果,嘗試不同的查詢條件 if (apps.length === 0) { - console.log('⚠️ 沒有找到 is_active = 1 的應用,嘗試其他查詢條件...'); - // 嘗試 is_active = TRUE const sqlTrue = sql.replace('WHERE a.is_active = 1', 'WHERE a.is_active = TRUE'); const appsTrue = await db.query(sqlTrue, params); - console.log('📊 is_active = TRUE 查詢結果:', appsTrue.length, '個應用'); // 嘗試 is_active = '1' const sqlString = sql.replace('WHERE a.is_active = 1', 'WHERE a.is_active = "1"'); const appsString = await db.query(sqlString, params); - console.log('📊 is_active = "1" 查詢結果:', appsString.length, '個應用'); // 嘗試沒有 is_active 條件 const sqlNoFilter = sql.replace('WHERE a.is_active = 1', 'WHERE 1=1'); const appsNoFilter = await db.query(sqlNoFilter, params); - console.log('📊 無 is_active 過濾查詢結果:', appsNoFilter.length, '個應用'); // 使用有結果的查詢 if (appsTrue.length > 0) { - console.log('✅ 使用 is_active = TRUE 的結果'); return NextResponse.json({ success: true, message: '可用應用列表獲取成功', data: appsTrue }); } else if (appsString.length > 0) { - console.log('✅ 使用 is_active = "1" 的結果'); return NextResponse.json({ success: true, message: '可用應用列表獲取成功', data: appsString }); } else if (appsNoFilter.length > 0) { - console.log('✅ 使用無過濾條件的結果'); return NextResponse.json({ success: true, message: '可用應用列表獲取成功', @@ -115,7 +62,6 @@ export async function GET(request: NextRequest) { } } - console.log('🚀 ========== 應用 API 執行完成 =========='); return NextResponse.json({ success: true, message: '可用應用列表獲取成功', @@ -130,4 +76,4 @@ export async function GET(request: NextRequest) { error: error instanceof Error ? error.message : '未知錯誤' }, { status: 500 }); } -} +} \ No newline at end of file diff --git a/app/api/test-db/route.ts b/app/api/test-db/route.ts index f1dfa50..1d80792 100644 --- a/app/api/test-db/route.ts +++ b/app/api/test-db/route.ts @@ -7,19 +7,14 @@ import { db } from '@/lib/database'; export async function GET(request: NextRequest) { try { - console.log('🧪 開始測試資料庫連接...'); - // 測試基本查詢 const result = await db.query('SELECT 1 as test'); - console.log('✅ 基本查詢成功:', result); // 測試競賽表 const competitions = await db.query('SELECT id, name, type FROM competitions WHERE is_active = TRUE LIMIT 3'); - console.log('✅ 競賽查詢成功:', competitions); // 測試評審表 const judges = await db.query('SELECT id, name, title FROM judges WHERE is_active = TRUE LIMIT 3'); - console.log('✅ 評審查詢成功:', judges); return NextResponse.json({ success: true, diff --git a/app/debug-scoring/page.tsx b/app/debug-scoring/page.tsx index d3f81ec..63d439e 100644 --- a/app/debug-scoring/page.tsx +++ b/app/debug-scoring/page.tsx @@ -14,7 +14,6 @@ export default function DebugScoringPage() { const addLog = (message: string) => { setLogs(prev => [...prev, `${new Date().toLocaleTimeString()}: ${message}`]) - console.log(message) } // 載入競賽列表 diff --git a/app/judge-scoring/page.tsx b/app/judge-scoring/page.tsx index a8593cd..778c783 100644 --- a/app/judge-scoring/page.tsx +++ b/app/judge-scoring/page.tsx @@ -63,7 +63,7 @@ export default function JudgeScoringPage() { try { // 獲取評審的評分任務 - const response = await fetch(`/api/judge/scoring-tasks?judgeId=${judgeId}`) + const response = await fetch(`/api/judge/scoring-tasks?judgeId=${judgeId}&competitionId=0fffae9a-9539-11f0-b5d9-6e36c63cdb98`) const data = await response.json() if (data.success) { @@ -89,7 +89,7 @@ export default function JudgeScoringPage() { const loadCompetitionRules = async () => { try { // 使用正確的競賽ID - const response = await fetch('/api/competitions/be47d842-91f1-11f0-8595-bd825523ae01/rules') + const response = await fetch('/api/competitions/0fffae9a-9539-11f0-b5d9-6e36c63cdb98/rules') const data = await response.json() if (data.success) { @@ -169,7 +169,7 @@ export default function JudgeScoringPage() { participantType: 'app', scores: scores, comments: comments.trim(), - competitionId: 'be47d842-91f1-11f0-8595-bd825523ae01', // 正確的競賽ID + competitionId: '0fffae9a-9539-11f0-b5d9-6e36c63cdb98', // 正確的競賽ID isEdit: selectedItem.status === "completed", // 如果是重新評分,標記為編輯模式 recordId: selectedItem.status === "completed" ? selectedItem.id : null }) @@ -298,11 +298,6 @@ export default function JudgeScoringPage() { )} - -
-

評審ID範例:j1, j2, j3, j4, j5

-

存取碼:judge2024

-
diff --git a/app/page.tsx b/app/page.tsx index 9b2f42c..797f6fd 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -217,7 +217,6 @@ export default function AIShowcasePlatform() { if (statsResponse.ok) { const statsData = await statsResponse.json() if (statsData.success) { - console.log(`載入應用 ${app.name} 的統計數據:`, statsData.data) return { ...app, ...statsData.data } } } @@ -365,7 +364,6 @@ export default function AIShowcasePlatform() { const handleTryApp = async (appId: string) => { await incrementViewCount(appId) addToRecentApps(appId) - console.log(`Opening app ${appId}`) } const getCompetitionTypeIcon = (type: string) => { diff --git a/app/test-manual-scoring/page.tsx b/app/test-manual-scoring/page.tsx index a6f633e..9b8c9a9 100644 --- a/app/test-manual-scoring/page.tsx +++ b/app/test-manual-scoring/page.tsx @@ -14,7 +14,6 @@ export default function TestManualScoringPage() { const loadCompetitionData = async () => { try { - console.log('🔍 開始載入競賽數據...') // 載入競賽信息 const competitionResponse = await fetch('/api/competitions/be4b0a71-91f1-11f0-bb38-4adff2d0e33e') @@ -22,7 +21,6 @@ export default function TestManualScoringPage() { if (competitionData.success) { setCompetition(competitionData.data.competition) - console.log('✅ 競賽載入成功:', competitionData.data.competition.name) } // 載入團隊數據 @@ -31,15 +29,6 @@ export default function TestManualScoringPage() { if (teamsData.success) { setTeams(teamsData.data.teams) - console.log('✅ 團隊載入成功:', teamsData.data.teams.length, '個團隊') - teamsData.data.teams.forEach((team: any) => { - console.log(` - ${team.name}: ${team.apps?.length || 0} 個APP`) - if (team.apps && team.apps.length > 0) { - team.apps.forEach((app: any) => { - console.log(` * ${app.name} (${app.id})`) - }) - } - }) } } catch (error) { diff --git a/components/admin/competition-management.tsx b/components/admin/competition-management.tsx index 63800af..ae8735b 100644 --- a/components/admin/competition-management.tsx +++ b/components/admin/competition-management.tsx @@ -860,32 +860,11 @@ export function CompetitionManagement() { // Get participants based on competition type const getParticipants = (competitionType: string) => { - console.log('🔍 getParticipants 調用:', { - competitionType, - dbTeamsLength: dbTeams.length, - teamsLength: teams.length, - isLoadingTeams - }) switch (competitionType) { case "individual": - console.log('🔍 個人賽APP數據:', { - availableAppsLength: availableApps.length, - availableApps: availableApps.slice(0, 2) - }) return availableApps case "team": // 總是使用 dbTeams,如果為空則返回空數組 - console.log('🔍 getParticipants 團隊數據:', { - dbTeamsLength: dbTeams.length, - dbTeams: dbTeams.slice(0, 2), // 只顯示前2個 - firstTeam: dbTeams[0] ? { - id: dbTeams[0].id, - name: dbTeams[0].name, - leader_name: dbTeams[0].leader_name, - member_count: dbTeams[0].member_count, - submissionDate: dbTeams[0].submissionDate - } : null - }) return dbTeams default: return [] @@ -898,12 +877,6 @@ export function CompetitionManagement() { let searchTerm = participantSearchTerm let departmentFilterValue = departmentFilter - console.log('🔍 getFilteredParticipants 調用:', { - competitionType, - participantsLength: participants.length, - searchTerm, - departmentFilterValue - }) // Use separate search terms for mixed competitions if (newCompetition.type === "mixed") { @@ -5319,22 +5292,6 @@ export function CompetitionManagement() { ) : ( getFilteredParticipants("team").map((participant) => { const isSelected = newCompetition.participatingTeams.includes(participant.id) - console.log('🔍 團隊數據調試 - 完整對象:', participant) - console.log('🔍 團隊數據調試 - 關鍵欄位:', { - name: participant.name, - leader_name: participant.leader_name, - leader: participant.leader, - member_count: participant.member_count, - submissionDate: participant.submissionDate, - hasLeaderName: 'leader_name' in participant, - hasMemberCount: 'member_count' in participant, - allKeys: Object.keys(participant) - }) - console.log('🔍 渲染測試:', { - leaderDisplay: participant.leader_name || participant.leader || '未知', - memberDisplay: participant.member_count || participant.memberCount || 0, - dateDisplay: participant.submissionDate ? new Date(participant.submissionDate).toLocaleDateString('zh-TW', { timeZone: 'Asia/Taipei', year: 'numeric', month: '2-digit', day: '2-digit' }).replace(/-/g, '/') : '未知' - }) return (
{ try { diff --git a/components/admin/scoring-management.tsx b/components/admin/scoring-management.tsx index 658d0ea..12edb5a 100644 --- a/components/admin/scoring-management.tsx +++ b/components/admin/scoring-management.tsx @@ -26,10 +26,11 @@ interface ScoringRecord { participantId: string participantName: string participantType: "individual" | "team" + teamName?: string scores: Record 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() {

- {scoringSummary ? scoringSummary.overallStats.totalJudges : progress.completed} + {competitionJudges.length}

評審總數

@@ -687,7 +778,7 @@ export function ScoringManagement() {

- {scoringSummary ? scoringSummary.overallStats.totalApps : progress.pending} + {competitionParticipants.length}

參賽APP數

@@ -697,7 +788,7 @@ export function ScoringManagement() {

- {scoringSummary ? scoringSummary.overallStats.completedScores : progress.percentage} + {progress.completed}

已完成評分

@@ -707,7 +798,7 @@ export function ScoringManagement() {

- {scoringSummary ? `${scoringSummary.overallStats.overallCompletionRate}%` : progress.total} + {progress.percentage}%

總完成率

@@ -718,15 +809,10 @@ export function ScoringManagement() {
評分進度 - - {scoringSummary ? - `${scoringSummary.overallStats.completedScores} / ${scoringSummary.overallStats.totalPossibleScores}` : - `${progress.completed} / ${progress.total}` - } - + {progress.completed} / {progress.total}
@@ -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) => (
{/* 項目標題和類型 */} -
-
+
+
{record.participantType === "individual" ? ( - + ) : ( - + )} - {record.participantName} +
+ + {record.participantType === "team" && record.teamName + ? `${record.teamName} - ${record.participantName}` + : record.participantName + } + +
- + {record.participantType === "individual" ? "個人" : "團隊"}
diff --git a/components/admin/team-management.tsx b/components/admin/team-management.tsx index 8cefac8..d42231f 100644 --- a/components/admin/team-management.tsx +++ b/components/admin/team-management.tsx @@ -79,11 +79,9 @@ export function TeamManagement() { if (data.success) { setApiTeams(data.data) } else { - console.error('獲取團隊數據失敗:', data.message) setError('獲取團隊數據失敗') } } catch (error) { - console.error('獲取團隊數據失敗:', error) setError('獲取團隊數據失敗') } finally { setIsLoadingTeams(false) @@ -105,11 +103,9 @@ export function TeamManagement() { if (data.success) { setAvailableUsers(data.data) } else { - console.error('獲取用戶列表失敗:', data.message) setError('獲取用戶列表失敗') } } catch (error) { - console.error('獲取用戶列表失敗:', error) setError('獲取用戶列表失敗') } finally { setIsLoadingUsers(false) diff --git a/components/app-detail-dialog.tsx b/components/app-detail-dialog.tsx index aae243e..0bb3bf9 100644 --- a/components/app-detail-dialog.tsx +++ b/components/app-detail-dialog.tsx @@ -198,11 +198,8 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp if (department) params.append('department', department) const url = `/api/apps/${app.id}/stats${params.toString() ? `?${params.toString()}` : ''}` - console.log('🔍 調用統計 API:', url) - console.log('🔍 應用 ID:', app.id) const response = await fetch(url) - console.log('🔍 API 響應狀態:', response.status, response.statusText) if (!response.ok) { console.error('❌ API 響應錯誤:', response.status, response.statusText) @@ -210,10 +207,8 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp } const data = await response.json() - console.log('🔍 API 響應數據:', data) if (data.success) { - console.log('📊 應用統計數據加載成功:', data.data) setAppStats(data.data) setCurrentRating(data.data.basic.rating) setReviewCount(data.data.basic.reviewCount) @@ -272,14 +267,12 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp }, [startDate, endDate, open, app.id, handleDateRangeChange]) const handleTryApp = async () => { - console.log('handleTryApp 被調用', { user: user?.id, appId: app.id }) if (user) { addToRecentApps(app.id.toString()) // 記錄用戶活動 try { - console.log('開始記錄用戶活動...') const response = await fetch('/api/user/activity', { method: 'POST', headers: { @@ -298,7 +291,6 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp }) if (response.ok) { - console.log('活動記錄成功') } else { console.error('活動記錄失敗:', response.status, response.statusText) } @@ -306,7 +298,6 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp console.error('記錄活動失敗:', error) } } else { - console.log('用戶未登入,跳過活動記錄') } // Increment view count when trying the app diff --git a/components/competition/competition-detail-dialog.tsx b/components/competition/competition-detail-dialog.tsx index 36e1414..d38cb23 100644 --- a/components/competition/competition-detail-dialog.tsx +++ b/components/competition/competition-detail-dialog.tsx @@ -59,7 +59,6 @@ export function CompetitionDetailDialog({ const handleTryApp = (appId: string) => { incrementViewCount(appId) addToRecentApps(appId) - console.log(`Opening app ${appId}`) } const getTypeColor = (type: string) => { diff --git a/components/competition/popularity-rankings.tsx b/components/competition/popularity-rankings.tsx index 5154bcb..5f5116b 100644 --- a/components/competition/popularity-rankings.tsx +++ b/components/competition/popularity-rankings.tsx @@ -94,17 +94,14 @@ export function PopularityRankings() { if (appsData.success) { // 合併個人應用和團隊應用 const allApps = appsData.data.apps || [] - console.log('📱 載入的應用數據:', allApps) setCompetitionApps(allApps) } if (teamsData.success) { const teams = teamsData.data.teams || [] - console.log('👥 載入的團隊數據:', teams) setCompetitionTeams(teams) } if (judgesData.success) { const judges = judgesData.data.judges || [] - console.log('👨‍⚖️ 載入的評審數據:', judges) setCompetitionJudges(judges) } } catch (error) { diff --git a/components/favorites-page.tsx b/components/favorites-page.tsx index 19cc42f..404178e 100644 --- a/components/favorites-page.tsx +++ b/components/favorites-page.tsx @@ -46,7 +46,6 @@ export function FavoritesPage() { }, [user]) const handleUseApp = async (app: any) => { - console.log('handleUseApp 被調用', { user: user?.id, appId: app.id, appName: app.name }) try { // Increment view count when using the app @@ -65,7 +64,6 @@ export function FavoritesPage() { if (response.ok) { // 記錄用戶活動 try { - console.log('開始記錄用戶活動...') const activityResponse = await fetch('/api/user/activity', { method: 'POST', headers: { @@ -84,7 +82,6 @@ export function FavoritesPage() { }) if (activityResponse.ok) { - console.log('活動記錄成功') } else { console.error('活動記錄失敗:', activityResponse.status, activityResponse.statusText) } @@ -98,7 +95,6 @@ export function FavoritesPage() { console.error('增加查看次數失敗:', response.status, response.statusText) } } else { - console.log('用戶未登入,跳過活動記錄') } } catch (error) { console.error('增加查看次數失敗:', error) @@ -109,7 +105,6 @@ export function FavoritesPage() { const url = app.appUrl.startsWith('http') ? app.appUrl : `https://${app.appUrl}` window.open(url, "_blank", "noopener,noreferrer") } else { - console.log(`Opening app: ${app.name}`) } } diff --git a/components/reviews/review-system.tsx b/components/reviews/review-system.tsx index 4bdf2a9..de8469d 100644 --- a/components/reviews/review-system.tsx +++ b/components/reviews/review-system.tsx @@ -353,7 +353,6 @@ export function ReviewSystem({ appId, appName, currentRating, onRatingUpdate }: {[5, 4, 3, 2, 1].map((rating) => { const count = reviews.filter((r) => r.rating === rating).length const percentage = (count / reviews.length) * 100 - console.log(`評分 ${rating}: count=${count}, percentage=${percentage}%`) // 調試信息 return (
diff --git a/contexts/competition-context.tsx b/contexts/competition-context.tsx index 0b79f4c..355700c 100644 --- a/contexts/competition-context.tsx +++ b/contexts/competition-context.tsx @@ -126,12 +126,10 @@ export function CompetitionProvider({ children }: { children: ReactNode }) { useEffect(() => { const loadCompetitions = async () => { try { - console.log('🔄 開始載入競賽數據...') // 載入所有競賽 const competitionsResponse = await fetch('/api/competitions') const competitionsData = await competitionsResponse.json() - console.log('📋 競賽API回應:', competitionsData) if (competitionsData.success) { if (competitionsData.data && competitionsData.data.length > 0) { @@ -141,22 +139,18 @@ export function CompetitionProvider({ children }: { children: ReactNode }) { judges: comp.judges || [] })) setCompetitions(competitionsWithJudges) - console.log('✅ 競賽數據載入成功:', competitionsWithJudges.length, '個競賽') } else { // 沒有競賽資料是正常情況,不報錯 setCompetitions([]) - console.log('ℹ️ 暫無競賽數據') } } else { // 沒有競賽數據是正常情況,不報錯 setCompetitions([]) - console.log('ℹ️ 暫無競賽數據') } // 載入當前競賽 const currentResponse = await fetch('/api/competitions/current') const currentData = await currentResponse.json() - console.log('🏆 當前競賽API回應:', currentData) if (currentData.success) { if (currentData.data) { @@ -166,36 +160,28 @@ export function CompetitionProvider({ children }: { children: ReactNode }) { judges: currentData.data.judges || [] } setCurrentCompetition(currentCompetitionWithJudges) - console.log('✅ 當前競賽載入成功:', currentCompetitionWithJudges.name) } else { // 沒有當前競賽是正常情況,不報錯 setCurrentCompetition(null) - console.log('ℹ️ 暫無當前競賽') } } else { // 沒有當前競賽是正常情況,不報錯 setCurrentCompetition(null) - console.log('ℹ️ 暫無當前競賽') } // 載入評審數據 - console.log('👨‍⚖️ 開始載入評審數據...') const judgesResponse = await fetch('/api/admin/judges') const judgesData = await judgesResponse.json() - console.log('評審API回應:', judgesData) if (judgesData.success) { if (judgesData.data && judgesData.data.length > 0) { setJudges(judgesData.data) - console.log('✅ 評審數據載入成功:', judgesData.data.length, '個評審') } else { setJudges([]) - console.log('ℹ️ 暫無評審數據') } } else { // 沒有評審數據是正常情況,不報錯 setJudges([]) - console.log('ℹ️ 暫無評審數據') } } catch (error) { console.error('❌ 載入競賽數據失敗:', error) @@ -283,12 +269,8 @@ export function CompetitionProvider({ children }: { children: ReactNode }) { } const deleteJudge = (id: string) => { - console.log('🗑️ Context deleteJudge 被調用,ID:', id) - console.log('🗑️ 刪除前的 judges 狀態:', judges.map(j => ({ id: j.id, name: j.name, is_active: j.is_active }))) - setJudges((prev) => { const filtered = prev.filter((judge) => judge.id !== id) - console.log('🗑️ 刪除後的 judges 狀態:', filtered.map(j => ({ id: j.id, name: j.name, is_active: j.is_active }))) return filtered }) diff --git a/lib/services/database-service.ts b/lib/services/database-service.ts index f0d6246..fe24385 100644 --- a/lib/services/database-service.ts +++ b/lib/services/database-service.ts @@ -613,14 +613,41 @@ export class UserService extends DatabaseServiceBase { `; const reviewStats = await this.queryOne(reviewStatsSql); - // 競賽統計 - const competitionStatsSql = ` - SELECT - COUNT(*) as total_competitions, - COUNT(CASE WHEN status = 'active' OR status = 'ongoing' THEN 1 END) as active_competitions - FROM competitions - `; - const competitionStats = await this.queryOne(competitionStatsSql); + // 競賽統計 - 使用動態計算的狀態 + const competitions = await CompetitionService.getAllCompetitions(); + + // 動態計算每個競賽的狀態 + const now = new Date(); + const competitionsWithCalculatedStatus = competitions.map(competition => { + const startDate = new Date(competition.start_date); + const endDate = new Date(competition.end_date); + + let calculatedStatus = competition.status; + + // 確保日期比較的準確性,使用 UTC 時間避免時區問題 + const nowUTC = new Date(now.getTime() + now.getTimezoneOffset() * 60000); + const startDateUTC = new Date(startDate.getTime() + startDate.getTimezoneOffset() * 60000); + const endDateUTC = new Date(endDate.getTime() + endDate.getTimezoneOffset() * 60000); + + // 根據實際日期計算狀態 + if (nowUTC < startDateUTC) { + calculatedStatus = 'upcoming'; // 即將開始 + } else if (nowUTC >= startDateUTC && nowUTC <= endDateUTC) { + calculatedStatus = 'active'; // 進行中 + } else if (nowUTC > endDateUTC) { + calculatedStatus = 'completed'; // 已完成 + } + + return { + ...competition, + status: calculatedStatus + }; + }); + + const competitionStats = { + total_competitions: competitionsWithCalculatedStatus.length, + active_competitions: competitionsWithCalculatedStatus.filter(c => c.status === 'active').length + }; // 計算增長率(與上個月比較) const lastMonthUsersSql = ` @@ -903,7 +930,6 @@ export class JudgeService extends DatabaseServiceBase { const fields = Object.keys(updates).filter(key => key !== 'id' && key !== 'created_at'); if (fields.length === 0) { - console.log('沒有字段需要更新'); return true; // 沒有需要更新的字段,視為成功 } @@ -916,11 +942,7 @@ export class JudgeService extends DatabaseServiceBase { }); const sql = `UPDATE judges SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`; - console.log('執行 SQL:', sql); - console.log('參數:', [...values, id]); - const result = await DatabaseServiceBase.safeUpdate(sql, [...values, id]); - console.log('更新結果:', result); return result.affectedRows > 0; } @@ -938,38 +960,59 @@ export class JudgeService extends DatabaseServiceBase { // 獲取評審的評分任務 static async getJudgeScoringTasks(judgeId: string, competitionId?: string): Promise { - let sql = ` - SELECT DISTINCT - a.id, - a.name, - 'app' as type, - 'individual' as participant_type, - COALESCE(js.total_score, 0) as score, - CASE - WHEN js.total_score > 0 THEN 'completed' - ELSE 'pending' - END as status, - js.submitted_at, - t.name as team_name, - CONCAT(COALESCE(t.name, '未知團隊'), ' - ', a.name) as display_name - FROM apps a - LEFT JOIN teams t ON a.team_id = t.id - LEFT JOIN competition_apps ca ON a.id = ca.app_id - LEFT JOIN judge_scores js ON a.id = js.app_id AND js.judge_id = ? - WHERE ca.competition_id = ? - `; - - const params = [judgeId]; + let sql: string; + let params: any[]; if (competitionId) { - params.push(competitionId); + // 獲取特定競賽的評分任務 + sql = ` + SELECT DISTINCT + a.id, + a.name, + 'app' as type, + 'individual' as participant_type, + COALESCE(js.total_score, 0) as score, + CASE + WHEN js.total_score > 0 THEN 'completed' + ELSE 'pending' + END as status, + js.submitted_at, + t.name as team_name, + a.name as display_name + FROM apps a + LEFT JOIN teams t ON a.team_id = t.id + LEFT JOIN competition_apps ca ON a.id = ca.app_id + LEFT JOIN judge_scores js ON a.id = js.app_id AND js.judge_id = ? + WHERE ca.competition_id = ? + ORDER BY a.name + `; + params = [judgeId, competitionId]; } else { - // 如果沒有指定競賽,獲取所有競賽的任務 - sql = sql.replace('WHERE ca.competition_id = ?', 'WHERE ca.competition_id IS NOT NULL'); + // 獲取所有競賽的任務 + sql = ` + SELECT DISTINCT + a.id, + a.name, + 'app' as type, + 'individual' as participant_type, + COALESCE(js.total_score, 0) as score, + CASE + WHEN js.total_score > 0 THEN 'completed' + ELSE 'pending' + END as status, + js.submitted_at, + t.name as team_name, + a.name as display_name + FROM apps a + LEFT JOIN teams t ON a.team_id = t.id + LEFT JOIN competition_apps ca ON a.id = ca.app_id + LEFT JOIN judge_scores js ON a.id = js.app_id AND js.judge_id = ? + WHERE ca.competition_id IS NOT NULL + ORDER BY a.name + `; + params = [judgeId]; } - sql += ' ORDER BY a.name'; - const results = await DatabaseServiceBase.safeQuery(sql, params); return results; } @@ -1002,7 +1045,6 @@ export class TeamService extends DatabaseServiceBase { ]; const result = await DatabaseServiceBase.safeInsert(sql, params); - console.log('團隊創建結果:', result); return id; } @@ -1025,13 +1067,6 @@ export class TeamService extends DatabaseServiceBase { ORDER BY t.created_at DESC `; const results = await DatabaseServiceBase.safeQuery(sql); - console.log('🔍 getAllTeams 查詢結果:', results.slice(0, 2).map(r => ({ - id: r.id, - name: r.name, - leader_name: r.leader_name, - member_count: r.member_count, - submissionDate: r.submissionDate - }))); return results; } @@ -1291,10 +1326,7 @@ export class TeamService extends DatabaseServiceBase { WHERE a.team_id = ? AND a.is_active = 1 ORDER BY a.created_at DESC `; - console.log('📝 getTeamApps SQL:', sql); - console.log('📝 getTeamApps 參數:', [teamId]); const results = await DatabaseServiceBase.safeQuery(sql, [teamId]); - console.log('📊 getTeamApps 結果:', results.length, '個應用'); return results; } @@ -1933,15 +1965,6 @@ export class CompetitionService extends DatabaseServiceBase { const startDateUTC = new Date(startDate.getTime() + startDate.getTimezoneOffset() * 60000); const endDateUTC = new Date(endDate.getTime() + endDate.getTimezoneOffset() * 60000); - console.log('🔍 競賽狀態計算:', { - competitionId, - name: competition.name, - now: nowUTC.toISOString(), - startDate: startDateUTC.toISOString(), - endDate: endDateUTC.toISOString(), - originalStatus: competition.status - }); - // 根據實際日期計算狀態 if (nowUTC < startDateUTC) { calculatedStatus = 'upcoming'; // 即將開始 @@ -1950,8 +1973,6 @@ export class CompetitionService extends DatabaseServiceBase { } else if (nowUTC > endDateUTC) { calculatedStatus = 'completed'; // 已完成 } - - console.log('🔍 計算後的狀態:', calculatedStatus); // 轉換字段名稱以匹配前端期望的格式 return { @@ -2977,14 +2998,41 @@ export class AppService extends DatabaseServiceBase { `; const reviewStats = await this.queryOne(reviewStatsSql); - // 競賽統計 - const competitionStatsSql = ` - SELECT - COUNT(*) as total_competitions, - COUNT(CASE WHEN status = 'active' OR status = 'ongoing' THEN 1 END) as active_competitions - FROM competitions - `; - const competitionStats = await this.queryOne(competitionStatsSql); + // 競賽統計 - 使用動態計算的狀態 + const competitions = await CompetitionService.getAllCompetitions(); + + // 動態計算每個競賽的狀態 + const now = new Date(); + const competitionsWithCalculatedStatus = competitions.map(competition => { + const startDate = new Date(competition.start_date); + const endDate = new Date(competition.end_date); + + let calculatedStatus = competition.status; + + // 確保日期比較的準確性,使用 UTC 時間避免時區問題 + const nowUTC = new Date(now.getTime() + now.getTimezoneOffset() * 60000); + const startDateUTC = new Date(startDate.getTime() + startDate.getTimezoneOffset() * 60000); + const endDateUTC = new Date(endDate.getTime() + endDate.getTimezoneOffset() * 60000); + + // 根據實際日期計算狀態 + if (nowUTC < startDateUTC) { + calculatedStatus = 'upcoming'; // 即將開始 + } else if (nowUTC >= startDateUTC && nowUTC <= endDateUTC) { + calculatedStatus = 'active'; // 進行中 + } else if (nowUTC > endDateUTC) { + calculatedStatus = 'completed'; // 已完成 + } + + return { + ...competition, + status: calculatedStatus + }; + }); + + const competitionStats = { + total_competitions: competitionsWithCalculatedStatus.length, + active_competitions: competitionsWithCalculatedStatus.filter(c => c.status === 'active').length + }; // 計算增長率(與上個月比較) const lastMonthUsersSql = ` @@ -3004,8 +3052,6 @@ export class AppService extends DatabaseServiceBase { totalUsers: userStats.totalUsers, activeUsers: userStats.activeUsers, totalApps: appStats?.total_apps || 0, - activeApps: appStats?.active_apps || 0, - inactiveApps: appStats?.inactive_apps || 0, totalCompetitions: competitionStats?.total_competitions || 0, totalReviews: reviewStats?.total_reviews || 0, totalViews: appStats?.total_views || 0, @@ -3578,7 +3624,6 @@ export class ScoringService extends DatabaseServiceBase { try { const result = await DatabaseServiceBase.safeQuery(sql, [competitionId]); - console.log('🔍 競賽規則查詢結果:', result); return result; } catch (error) { console.error('❌ 獲取競賽規則失敗:', error); @@ -3867,7 +3912,6 @@ export class ScoringService extends DatabaseServiceBase { }; }> { try { - console.log('🔍 獲取評分完成度匯總,competitionId:', competitionId); // 獲取競賽的評審列表 - 先嘗試從關聯表獲取,如果沒有則獲取所有評審 let judgesResult = await DatabaseServiceBase.safeQuery(`