建立檔案

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

422
contexts/auth-context.tsx Normal file
View File

@@ -0,0 +1,422 @@
"use client"
import { createContext, useContext, useState, useEffect, type ReactNode } from "react"
interface User {
id: string
name: string
email: string
avatar?: string
department: string
role: "user" | "developer" | "admin"
joinDate: string
favoriteApps: string[]
recentApps: string[]
totalLikes: number
totalViews: number
}
interface AppLike {
appId: string
userId: string
likedAt: string
}
interface AuthContextType {
user: User | null
login: (email: string, password: string) => Promise<boolean>
register: (userData: RegisterData) => Promise<boolean>
logout: () => void
updateProfile: (userData: Partial<User>) => Promise<boolean>
toggleFavorite: (appId: string) => Promise<boolean>
isFavorite: (appId: string) => boolean
addToRecentApps: (appId: string) => void
getAppLikes: (appId: string) => number
incrementViewCount: (appId: string) => void
getViewCount: (appId: string) => number
updateAppRating: (appId: string, rating: number) => void
getAppRating: (appId: string) => number
canSubmitApp: () => boolean
canAccessAdmin: () => boolean
isLoading: boolean
// New like functionality
toggleLike: (appId: string) => Promise<boolean>
hasLikedToday: (appId: string) => boolean
getAppLikesInPeriod: (appId: string, startDate: string, endDate: string) => number
getUserLikeHistory: () => Array<{ appId: string; date: string }>
getLikeCount: (appId: string) => number
likeApp: (appId: string) => Promise<boolean>
}
interface RegisterData {
name: string
email: string
password: string
department: string
}
const AuthContext = createContext<AuthContextType | undefined>(undefined)
// Mock users data with new role system
const mockUsers: User[] = []
// Global app likes counter - in a real app this would be in a database
const appLikesCounter: Record<string, number> = {}
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [isLoading, setIsLoading] = useState(true)
// View count state with localStorage persistence
const [appViews, setAppViews] = useState<Record<string, number>>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("appViews")
return saved ? JSON.parse(saved) : {}
}
return {}
})
// App ratings state with localStorage persistence
const [appRatings, setAppRatings] = useState<Record<string, number>>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("appRatings")
return saved ? JSON.parse(saved) : {}
}
return {}
})
const [appLikes, setAppLikes] = useState<Record<string, AppLike[]>>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("appLikes")
return saved ? JSON.parse(saved) : {}
}
return {}
})
// New like system state with localStorage persistence
const [userLikes, setUserLikes] = useState<Record<string, Array<{ appId: string; date: string }>>>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("userLikes")
return saved ? JSON.parse(saved) : {}
}
return {}
})
// App likes with date tracking
const [appLikesOld, setAppLikesOld] = useState<Record<string, Array<{ userId: string; date: string }>>>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("appLikesOld")
return saved ? JSON.parse(saved) : {}
}
return {}
})
useEffect(() => {
// Check for stored user session
const storedUser = localStorage.getItem("user")
if (storedUser) {
setUser(JSON.parse(storedUser))
}
setIsLoading(false)
}, [])
// Save likes to localStorage when they change
useEffect(() => {
if (typeof window !== "undefined") {
localStorage.setItem("userLikes", JSON.stringify(userLikes))
localStorage.setItem("appLikesOld", JSON.stringify(appLikesOld))
}
}, [userLikes, appLikesOld])
const login = async (email: string, password: string): Promise<boolean> => {
setIsLoading(true)
// Simulate API call
await new Promise((resolve) => setTimeout(resolve, 1000))
const foundUser = mockUsers.find((u) => u.email === email)
if (foundUser && password === "password123") {
setUser(foundUser)
localStorage.setItem("user", JSON.stringify(foundUser))
setIsLoading(false)
return true
}
setIsLoading(false)
return false
}
const register = async (userData: RegisterData): Promise<boolean> => {
setIsLoading(true)
// Simulate API call
await new Promise((resolve) => setTimeout(resolve, 1000))
// Check if user already exists
const existingUser = mockUsers.find((u) => u.email === userData.email)
if (existingUser) {
setIsLoading(false)
return false
}
const newUser: User = {
id: Date.now().toString(),
name: userData.name,
email: userData.email,
department: userData.department,
role: "user",
joinDate: new Date().toISOString().split("T")[0],
favoriteApps: [],
recentApps: [],
totalLikes: 0,
totalViews: 0,
}
mockUsers.push(newUser)
setUser(newUser)
localStorage.setItem("user", JSON.stringify(newUser))
setIsLoading(false)
return true
}
const logout = () => {
setUser(null)
localStorage.removeItem("user")
}
const updateProfile = async (userData: Partial<User>): Promise<boolean> => {
if (!user) return false
setIsLoading(true)
// Simulate API call
await new Promise((resolve) => setTimeout(resolve, 1000))
const updatedUser = { ...user, ...userData }
setUser(updatedUser)
localStorage.setItem("user", JSON.stringify(updatedUser))
// Update in mock data
const userIndex = mockUsers.findIndex((u) => u.id === user.id)
if (userIndex !== -1) {
mockUsers[userIndex] = updatedUser
}
setIsLoading(false)
return true
}
const toggleFavorite = async (appId: string): Promise<boolean> => {
if (!user) return false
const isFavorited = user.favoriteApps.includes(appId)
const updatedFavorites = isFavorited
? user.favoriteApps.filter((id) => id !== appId)
: [...user.favoriteApps, appId]
// Update global likes counter (keeping for backward compatibility)
if (isFavorited) {
appLikesCounter[appId] = Math.max(0, appLikesCounter[appId] - 1)
} else {
appLikesCounter[appId] = (appLikesCounter[appId] || 0) + 1
}
const success = await updateProfile({
favoriteApps: updatedFavorites,
})
return success
}
const isFavorite = (appId: string): boolean => {
return user?.favoriteApps.includes(appId) || false
}
const addToRecentApps = (appId: string): void => {
if (!user) return
const updatedRecent = [appId, ...user.recentApps.filter((id) => id !== appId)].slice(0, 10)
updateProfile({
recentApps: updatedRecent,
})
}
const getAppLikes = (appId: string): number => {
return appLikesCounter[appId] || 0
}
// New like functionality
const toggleLike = async (appId: string): Promise<boolean> => {
if (!user) return false
const today = new Date().toISOString().split("T")[0]
const userLikeHistory = userLikes[user.id] || []
// Check if user has already liked this app today
const hasLikedTodayOld = userLikeHistory.some((like) => like.appId === appId && like.date === today)
if (hasLikedTodayOld) {
return false // Cannot like again today
}
// Add like to user's history
const updatedUserLikes = {
...userLikes,
[user.id]: [...userLikeHistory, { appId, date: today }],
}
setUserLikes(updatedUserLikes)
// Add like to app's likes
const appLikeHistory = appLikesOld[appId] || []
const updatedAppLikes = {
...appLikesOld,
[appId]: [...appLikeHistory, { userId: user.id, date: today }],
}
setAppLikesOld(updatedAppLikes)
return true
}
const hasLikedTodayOld = (appId: string): boolean => {
if (!user) return false
const today = new Date().toISOString().split("T")[0]
const userLikeHistory = userLikes[user.id] || []
return userLikeHistory.some((like) => like.appId === appId && like.date === today)
}
const getAppLikesInPeriod = (appId: string, startDate: string, endDate: string): number => {
const appLikeHistory = appLikesOld[appId] || []
return appLikeHistory.filter((like) => {
return like.date >= startDate && like.date <= endDate
}).length
}
const getUserLikeHistory = (): Array<{ appId: string; date: string }> => {
if (!user) return []
return userLikes[user.id] || []
}
// View count functionality
const incrementViewCount = (appId: string): void => {
setAppViews((prev) => {
const newViews = { ...prev, [appId]: (prev[appId] || 0) + 1 }
if (typeof window !== "undefined") {
localStorage.setItem("appViews", JSON.stringify(newViews))
}
return newViews
})
}
const getViewCount = (appId: string): number => {
return appViews[appId] || 0
}
// Rating functionality
const updateAppRating = (appId: string, rating: number): void => {
setAppRatings((prev) => {
const newRatings = { ...prev, [appId]: rating }
if (typeof window !== "undefined") {
localStorage.setItem("appRatings", JSON.stringify(newRatings))
}
return newRatings
})
}
const getAppRating = (appId: string): number => {
return appRatings[appId] || 0
}
const getLikeCount = (appId: string): number => {
return appLikes[appId]?.length || 0
}
const hasLikedToday = (appId: string): boolean => {
if (!user) return false
const today = new Date().toISOString().split("T")[0]
return appLikes[appId]?.some((like) => like.userId === user.id && like.likedAt.startsWith(today)) || false
}
const likeApp = async (appId: string): Promise<boolean> => {
if (!user) return false
const today = new Date().toISOString().split("T")[0]
// Check if user already liked today
if (hasLikedToday(appId)) return false
const newLike: AppLike = {
appId,
userId: user.id,
likedAt: new Date().toISOString(),
}
setAppLikes((prev) => {
const updatedLikes = { ...prev }
if (!updatedLikes[appId]) {
updatedLikes[appId] = []
}
updatedLikes[appId] = [...updatedLikes[appId], newLike]
if (typeof window !== "undefined") {
localStorage.setItem("appLikes", JSON.stringify(updatedLikes))
}
return updatedLikes
})
return true
}
// Permission check functions
const canSubmitApp = (): boolean => {
return user?.role === "developer"
}
const canAccessAdmin = (): boolean => {
return user?.role === "admin"
}
return (
<AuthContext.Provider
value={{
user,
login,
register,
logout,
updateProfile,
toggleFavorite,
isFavorite,
addToRecentApps,
getAppLikes,
incrementViewCount,
getViewCount,
updateAppRating,
getAppRating,
canSubmitApp,
canAccessAdmin,
isLoading,
// New like functionality
toggleLike,
hasLikedTodayOld,
getAppLikesInPeriod,
getUserLikeHistory,
getLikeCount,
hasLikedToday,
likeApp,
}}
>
{children}
</AuthContext.Provider>
)
}
export function useAuth() {
const context = useContext(AuthContext)
if (context === undefined) {
throw new Error("useAuth must be used within an AuthProvider")
}
return context
}

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
}