建立檔案

This commit is contained in:
2025-08-05 08:22:44 +08:00
commit 042d03aff7
122 changed files with 34763 additions and 0 deletions

View File

@@ -0,0 +1,720 @@
"use client"
import { createContext, useContext, useState, useEffect, type ReactNode } from "react"
import type { Judge, JudgeScore, Competition, Award, Team, Proposal, ProposalJudgeScore } from "@/types/competition"
interface CompetitionContextType {
// Judges
judges: Judge[]
addJudge: (judge: Omit<Judge, "id">) => void
updateJudge: (id: string, updates: Partial<Judge>) => void
deleteJudge: (id: string) => void
// Competitions
competitions: Competition[]
currentCompetition: Competition | null
setCurrentCompetition: (competition: Competition | null) => void
addCompetition: (competition: Omit<Competition, "id">) => void
updateCompetition: (id: string, updates: Partial<Competition>) => void
deleteCompetition: (id: string) => void
// Teams
teams: Team[]
addTeam: (team: Omit<Team, "id">) => void
updateTeam: (id: string, updates: Partial<Team>) => void
getTeamById: (id: string) => Team | undefined
// Proposals
proposals: Proposal[]
addProposal: (proposal: Omit<Proposal, "id">) => void
updateProposal: (id: string, updates: Partial<Proposal>) => void
getProposalById: (id: string) => Proposal | undefined
// Judge Scores
judgeScores: JudgeScore[]
proposalJudgeScores: ProposalJudgeScore[]
addJudgeScore: (score: Omit<JudgeScore, "submittedAt">) => void
addProposalJudgeScore: (score: Omit<ProposalJudgeScore, "submittedAt">) => void
submitJudgeScore: (score: Omit<JudgeScore, "submittedAt">) => void
submitProposalJudgeScore: (score: Omit<ProposalJudgeScore, "submittedAt">) => void
getAppJudgeScores: (appId: string) => JudgeScore[]
getProposalJudgeScores: (proposalId: string) => ProposalJudgeScore[]
getAppAverageScore: (appId: string) => number
getProposalAverageScore: (proposalId: string) => number
getAppDetailedScores: (appId: string) => {
innovation: number
technical: number
usability: number
presentation: number
impact: number
total: number
judgeCount: number
}
getProposalDetailedScores: (proposalId: string) => {
problemIdentification: number
solutionFeasibility: number
innovation: number
impact: number
presentation: number
total: number
judgeCount: number
}
// Awards
awards: Award[]
addAward: (award: Omit<Award, "id">) => void
getAwardsByYear: (year: number) => Award[]
getAwardsByMonth: (year: number, month: number) => Award[]
// Rankings
getCompetitionRankings: (competitionId?: string) => Array<{
appId?: string
proposalId?: string
teamId?: string
appName?: string
proposalTitle?: string
teamName?: string
creator: string
totalScore: number
rank: number
scores: any
competitionType: "individual" | "team" | "proposal"
}>
// Add new filtering functions
getAwardsByCompetitionType: (competitionType?: string) => Award[]
getAwardsByCategory: (category?: string) => Award[]
getTopRankingAwards: () => Award[]
getPopularityAwards: () => Award[]
getPopularityRankings: (competitionId?: string) => Array<{
id: string
name: string
creator: string
department: string
type: string
likes: number
rank: number
}>
}
const CompetitionContext = createContext<CompetitionContextType | undefined>(undefined)
// Mock data
const mockJudges: Judge[] = []
// Mock teams data
const mockTeams: Team[] = []
// Mock proposals data
const mockProposals: Proposal[] = []
// 競賽資料
const mockCompetitions: Competition[] = []
// Mock app likes counter
const appLikesCounter: Record<string, number> = {}
export function CompetitionProvider({ children }: { children: ReactNode }) {
const [judges, setJudges] = useState<Judge[]>(mockJudges)
const [competitions, setCompetitions] = useState<Competition[]>(mockCompetitions)
const [currentCompetition, setCurrentCompetition] = useState<Competition | null>(null)
const [teams, setTeams] = useState<Team[]>(mockTeams)
const [proposals, setProposals] = useState<Proposal[]>(mockProposals)
// Load judge scores from localStorage
const [judgeScores, setJudgeScores] = useState<JudgeScore[]>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("judgeScores")
if (saved) {
return JSON.parse(saved)
}
}
// 評分資料
const mockScores: JudgeScore[] = []
return mockScores
})
// Load proposal judge scores from localStorage
const [proposalJudgeScores, setProposalJudgeScores] = useState<ProposalJudgeScore[]>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("proposalJudgeScores")
if (saved) {
return JSON.parse(saved)
}
}
// 提案評分資料
const mockProposalScores: ProposalJudgeScore[] = []
return mockProposalScores
})
// Load awards from localStorage
const [awards, setAwards] = useState<Award[]>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("competitionAwards")
if (saved) {
return JSON.parse(saved)
}
}
// 獎項資料
const mockAwards = []
return mockAwards
})
// Save to localStorage when data changes
useEffect(() => {
if (typeof window !== "undefined") {
localStorage.setItem("judgeScores", JSON.stringify(judgeScores))
}
}, [judgeScores])
useEffect(() => {
if (typeof window !== "undefined") {
localStorage.setItem("proposalJudgeScores", JSON.stringify(proposalJudgeScores))
}
}, [proposalJudgeScores])
useEffect(() => {
if (typeof window !== "undefined") {
localStorage.setItem("competitionAwards", JSON.stringify(awards))
}
}, [awards])
const addJudge = (judge: Omit<Judge, "id">) => {
const newJudge: Judge = {
...judge,
id: `j${Date.now()}`,
}
setJudges((prev) => [...prev, newJudge])
}
const updateJudge = (id: string, updates: Partial<Judge>) => {
setJudges((prev) => prev.map((judge) => (judge.id === id ? { ...judge, ...updates } : judge)))
}
const deleteJudge = (id: string) => {
setJudges((prev) => prev.filter((judge) => judge.id !== id))
setCompetitions((prev) =>
prev.map((comp) => ({
...comp,
judges: comp.judges.filter((judgeId) => judgeId !== id),
})),
)
setJudgeScores((prev) => prev.filter((score) => score.judgeId !== id))
setProposalJudgeScores((prev) => prev.filter((score) => score.judgeId !== id))
}
const addCompetition = (competition: Omit<Competition, "id">) => {
const newCompetition: Competition = {
...competition,
id: `c${Date.now()}`,
}
setCompetitions((prev) => [...prev, newCompetition])
}
const updateCompetition = (id: string, updates: Partial<Competition>) => {
setCompetitions((prev) => prev.map((comp) => (comp.id === id ? { ...comp, ...updates } : comp)))
if (currentCompetition?.id === id) {
setCurrentCompetition((prev) => (prev ? { ...prev, ...updates } : null))
}
}
const deleteCompetition = (id: string) => {
setCompetitions((prev) => prev.filter((comp) => comp.id !== id))
if (currentCompetition?.id === id) {
setCurrentCompetition(null)
}
}
const addTeam = (team: Omit<Team, "id">) => {
const newTeam: Team = {
...team,
id: `t${Date.now()}`,
}
setTeams((prev) => [...prev, newTeam])
}
const updateTeam = (id: string, updates: Partial<Team>) => {
setTeams((prev) => prev.map((team) => (team.id === id ? { ...team, ...updates } : team)))
}
const getTeamById = (id: string): Team | undefined => {
return teams.find((team) => team.id === id)
}
const addProposal = (proposal: Omit<Proposal, "id">) => {
const newProposal: Proposal = {
...proposal,
id: `p${Date.now()}`,
}
setProposals((prev) => [...prev, newProposal])
}
const updateProposal = (id: string, updates: Partial<Proposal>) => {
setProposals((prev) => prev.map((proposal) => (proposal.id === id ? { ...proposal, ...updates } : proposal)))
}
const getProposalById = (id: string): Proposal | undefined => {
return proposals.find((proposal) => proposal.id === id)
}
const addJudgeScore = (score: Omit<JudgeScore, "submittedAt">) => {
const newScore: JudgeScore = {
...score,
submittedAt: new Date().toISOString(),
}
const filteredScores = judgeScores.filter((s) => !(s.judgeId === score.judgeId && s.appId === score.appId))
setJudgeScores([...filteredScores, newScore])
}
const addProposalJudgeScore = (score: Omit<ProposalJudgeScore, "submittedAt">) => {
const newScore: ProposalJudgeScore = {
...score,
submittedAt: new Date().toISOString(),
}
const filteredScores = proposalJudgeScores.filter(
(s) => !(s.judgeId === score.judgeId && s.proposalId === score.proposalId),
)
setProposalJudgeScores([...filteredScores, newScore])
}
const submitJudgeScore = (score: Omit<JudgeScore, "submittedAt">) => {
addJudgeScore(score)
}
const submitProposalJudgeScore = (score: Omit<ProposalJudgeScore, "submittedAt">) => {
addProposalJudgeScore(score)
}
const getAppJudgeScores = (appId: string): JudgeScore[] => {
return judgeScores.filter((score) => score.appId === appId)
}
const getProposalJudgeScores = (proposalId: string): ProposalJudgeScore[] => {
return proposalJudgeScores.filter((score) => score.proposalId === proposalId)
}
const getAppAverageScore = (appId: string): number => {
const scores = getAppJudgeScores(appId)
if (scores.length === 0) return 0
const totalScore = scores.reduce((sum, score) => {
const scoreSum = Object.values(score.scores).reduce((a, b) => a + b, 0)
return sum + scoreSum
}, 0)
return Number((totalScore / (scores.length * 5)).toFixed(1))
}
const getProposalAverageScore = (proposalId: string): number => {
const scores = getProposalJudgeScores(proposalId)
if (scores.length === 0) return 0
const totalScore = scores.reduce((sum, score) => {
const scoreSum = Object.values(score.scores).reduce((a, b) => a + b, 0)
return sum + scoreSum
}, 0)
return Number((totalScore / (scores.length * 5)).toFixed(1))
}
const getAppDetailedScores = (appId: string) => {
const scores = getAppJudgeScores(appId)
if (scores.length === 0) {
return {
innovation: 0,
technical: 0,
usability: 0,
presentation: 0,
impact: 0,
total: 0,
judgeCount: 0,
}
}
const totals = scores.reduce(
(acc, score) => ({
innovation: acc.innovation + score.scores.innovation,
technical: acc.technical + score.scores.technical,
usability: acc.usability + score.scores.usability,
presentation: acc.presentation + score.scores.presentation,
impact: acc.impact + score.scores.impact,
}),
{ innovation: 0, technical: 0, usability: 0, presentation: 0, impact: 0 },
)
const judgeCount = scores.length
const averages = {
innovation: Number((totals.innovation / judgeCount).toFixed(1)),
technical: Number((totals.technical / judgeCount).toFixed(1)),
usability: Number((totals.usability / judgeCount).toFixed(1)),
presentation: Number((totals.presentation / judgeCount).toFixed(1)),
impact: Number((totals.impact / judgeCount).toFixed(1)),
}
const total = Number(
(
(totals.innovation + totals.technical + totals.usability + totals.presentation + totals.impact) /
(judgeCount * 5)
).toFixed(1),
)
return {
...averages,
total,
judgeCount,
}
}
const getProposalDetailedScores = (proposalId: string) => {
const scores = getProposalJudgeScores(proposalId)
if (scores.length === 0) {
return {
problemIdentification: 0,
solutionFeasibility: 0,
innovation: 0,
impact: 0,
presentation: 0,
total: 0,
judgeCount: 0,
}
}
const totals = scores.reduce(
(acc, score) => ({
problemIdentification: acc.problemIdentification + score.scores.problemIdentification,
solutionFeasibility: acc.solutionFeasibility + score.scores.solutionFeasibility,
innovation: acc.innovation + score.scores.innovation,
impact: acc.impact + score.scores.impact,
presentation: acc.presentation + score.scores.presentation,
}),
{ problemIdentification: 0, solutionFeasibility: 0, innovation: 0, impact: 0, presentation: 0 },
)
const judgeCount = scores.length
const averages = {
problemIdentification: Number((totals.problemIdentification / judgeCount).toFixed(1)),
solutionFeasibility: Number((totals.solutionFeasibility / judgeCount).toFixed(1)),
innovation: Number((totals.innovation / judgeCount).toFixed(1)),
impact: Number((totals.impact / judgeCount).toFixed(1)),
presentation: Number((totals.presentation / judgeCount).toFixed(1)),
}
const total = Number(
(
(totals.problemIdentification +
totals.solutionFeasibility +
totals.innovation +
totals.impact +
totals.presentation) /
(judgeCount * 5)
).toFixed(1),
)
return {
...averages,
total,
judgeCount,
}
}
const addAward = (award: Omit<Award, "id">) => {
const newAward: Award = {
...award,
id: `a${Date.now()}`,
}
setAwards((prev) => [...prev, newAward])
}
const getAwardsByYear = (year: number): Award[] => {
return awards.filter((award) => award.year === year)
}
const getAwardsByMonth = (year: number, month: number): Award[] => {
return awards.filter((award) => award.year === year && award.month === month)
}
const getCompetitionRankings = (competitionId?: string) => {
const targetCompetition = competitionId ? competitions.find((c) => c.id === competitionId) : currentCompetition
if (!targetCompetition) return []
const rankings: any[] = []
// Handle individual competitions
if (targetCompetition.type === "individual" || targetCompetition.type === "mixed") {
targetCompetition.participatingApps.forEach((appId) => {
const detailedScores = getAppDetailedScores(appId)
const appNames: Record<string, string> = {
"1": "智能對話助手",
"2": "圖像生成工具",
"3": "語音識別系統",
"4": "智能推薦引擎",
"5": "文本分析器",
"6": "AI創意寫作",
}
const creators: Record<string, string> = {
"1": "張小明",
"2": "李美華",
"3": "王大偉",
"4": "陳小芳",
"5": "劉志強",
"6": "黃小玲",
}
rankings.push({
appId,
appName: appNames[appId] || `應用 ${appId}`,
creator: creators[appId] || "未知",
totalScore: detailedScores.total,
scores: {
innovation: detailedScores.innovation,
technical: detailedScores.technical,
usability: detailedScores.usability,
presentation: detailedScores.presentation,
impact: detailedScores.impact,
},
rank: 0,
competitionType: "individual" as const,
})
})
}
// Handle team competitions
if (targetCompetition.type === "team" || targetCompetition.type === "mixed") {
targetCompetition.participatingTeams.forEach((teamId) => {
const team = getTeamById(teamId)
if (team) {
// Calculate team score based on their apps
let totalScore = 0
let appCount = 0
team.apps.forEach((appId) => {
const appScore = getAppDetailedScores(appId)
totalScore += appScore.total
appCount++
})
const averageScore = appCount > 0 ? totalScore / appCount : 0
rankings.push({
teamId,
teamName: team.name,
creator: `${team.members.find((m) => m.id === team.leader)?.name}團隊`,
totalScore: averageScore,
scores: {
teamwork: averageScore * 0.25,
technical: averageScore * 0.25,
innovation: averageScore * 0.25,
practical: averageScore * 0.25,
},
rank: 0,
competitionType: "team" as const,
appCount: team.apps.length,
totalLikes: team.totalLikes,
members: team.members,
})
}
})
}
// Handle proposal competitions
if (targetCompetition.type === "proposal" || targetCompetition.type === "mixed") {
targetCompetition.participatingProposals.forEach((proposalId) => {
const proposal = getProposalById(proposalId)
const detailedScores = getProposalDetailedScores(proposalId)
if (proposal) {
const team = getTeamById(proposal.teamId)
rankings.push({
proposalId,
proposalTitle: proposal.title,
creator: team?.name || "未知團隊",
totalScore: detailedScores.total,
scores: {
problemIdentification: detailedScores.problemIdentification,
solutionFeasibility: detailedScores.solutionFeasibility,
innovation: detailedScores.innovation,
impact: detailedScores.impact,
presentation: detailedScores.presentation,
},
rank: 0,
competitionType: "proposal" as const,
proposal,
team,
})
}
})
}
// Sort by total score and assign ranks within each competition type
const individualRankings = rankings
.filter((r) => r.competitionType === "individual")
.sort((a, b) => b.totalScore - a.totalScore)
.map((item, index) => ({ ...item, rank: index + 1 }))
const teamRankings = rankings
.filter((r) => r.competitionType === "team")
.sort((a, b) => b.totalScore - a.totalScore)
.map((item, index) => ({ ...item, rank: index + 1 }))
const proposalRankings = rankings
.filter((r) => r.competitionType === "proposal")
.sort((a, b) => b.totalScore - a.totalScore)
.map((item, index) => ({ ...item, rank: index + 1 }))
// Combine all rankings
return [...individualRankings, ...teamRankings, ...proposalRankings]
}
// Add new filtering functions
const getAwardsByCompetitionType = (competitionType?: string) => {
if (!competitionType || competitionType === "all") return awards
return awards.filter((award) => award.competitionType === competitionType)
}
const getAwardsByCategory = (category?: string) => {
if (!category || category === "all") return awards
return awards.filter((award) => award.category === category)
}
const getTopRankingAwards = () => {
return awards.filter((award) => award.rank > 0 && award.rank <= 3)
}
const getPopularityAwards = () => {
return awards.filter((award) => award.awardType === "popular")
}
const getPopularityRankings = (competitionId?: string) => {
const targetCompetition = competitionId ? competitions.find((c) => c.id === competitionId) : currentCompetition
if (!targetCompetition) return []
const rankings: any[] = []
// Get participants based on competition type
const participants =
targetCompetition.type === "individual"
? targetCompetition.participatingApps
: targetCompetition.type === "team"
? targetCompetition.participatingTeams
: targetCompetition.participatingProposals
// For each participant, calculate popularity based on likes
participants.forEach((participantId) => {
const appNames: Record<string, string> = {
"1": "智能對話助手",
"2": "圖像生成工具",
"3": "語音識別系統",
"4": "智能推薦引擎",
"5": "文本分析器",
"6": "AI創意寫作",
}
const creators: Record<string, string> = {
"1": "張小明",
"2": "李美華",
"3": "王大偉",
"4": "陳小芳",
"5": "劉志強",
"6": "黃小玲",
}
const departments: Record<string, string> = {
"1": "HQBU",
"2": "ITBU",
"3": "MBU1",
"4": "SBU",
"5": "HQBU",
"6": "ITBU",
}
const types: Record<string, string> = {
"1": "文字處理",
"2": "圖像生成",
"3": "語音辨識",
"4": "推薦系統",
"5": "文字處理",
"6": "文字處理",
}
// Get likes from appLikesCounter
const likes = appLikesCounter[participantId] || 0
rankings.push({
id: participantId,
name: appNames[participantId] || `應用 ${participantId}`,
creator: creators[participantId] || "未知",
department: departments[participantId] || "未知",
type: types[participantId] || "未知",
likes: likes,
rank: 0, // Will be calculated after sorting
})
})
// Sort by likes and assign ranks
return rankings.sort((a, b) => b.likes - a.likes).map((item, index) => ({ ...item, rank: index + 1 }))
}
// Add these functions to the context value
return (
<CompetitionContext.Provider
value={{
judges,
addJudge,
updateJudge,
deleteJudge,
competitions,
currentCompetition,
setCurrentCompetition,
addCompetition,
updateCompetition,
deleteCompetition,
teams,
addTeam,
updateTeam,
getTeamById,
proposals,
addProposal,
updateProposal,
getProposalById,
judgeScores,
proposalJudgeScores,
addJudgeScore,
addProposalJudgeScore,
submitJudgeScore,
submitProposalJudgeScore,
getAppJudgeScores,
getProposalJudgeScores,
getAppAverageScore,
getProposalAverageScore,
getAppDetailedScores,
getProposalDetailedScores,
awards,
addAward,
getAwardsByYear,
getAwardsByMonth,
getCompetitionRankings,
getAwardsByCompetitionType,
getAwardsByCategory,
getTopRankingAwards,
getPopularityAwards,
getPopularityRankings,
}}
>
{children}
</CompetitionContext.Provider>
)
}
export function useCompetition() {
const context = useContext(CompetitionContext)
if (context === undefined) {
throw new Error("useCompetition must be used within a CompetitionProvider")
}
return context
}