Files
ai-showcase-platform/components/competition/team-detail-dialog.tsx

388 lines
16 KiB
TypeScript

"use client"
import { useState } from "react"
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import {
Users,
Mail,
Eye,
Heart,
Trophy,
Star,
MessageSquare,
ImageIcon,
Mic,
TrendingUp,
Brain,
Zap,
ExternalLink,
Bot,
Code,
Database,
Palette,
Volume2,
Search,
BarChart3,
} from "lucide-react"
import { useAuth } from "@/contexts/auth-context"
import { LikeButton } from "@/components/like-button"
import { AppDetailDialog } from "@/components/app-detail-dialog"
interface TeamDetailDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
team: any
}
// 圖標映射函數
const getIconComponent = (iconName: string) => {
const iconMap: { [key: string]: any } = {
'Brain': Brain,
'Bot': Bot,
'Code': Code,
'Database': Database,
'Palette': Palette,
'Volume2': Volume2,
'Search': Search,
'BarChart3': BarChart3,
'Mic': Mic,
'ImageIcon': ImageIcon,
'MessageSquare': MessageSquare,
'Zap': Zap,
'TrendingUp': TrendingUp,
};
return iconMap[iconName] || Brain;
}
// App data for team apps - get from team data
const getAppDetails = (appId: string, team: any) => {
const appDetail = team.appsDetails?.find((app: any) => app.id === appId);
if (appDetail) {
return {
id: appDetail.id,
name: appDetail.name || "未命名應用",
type: appDetail.type || "未知類型",
description: appDetail.description || "無描述",
icon: getIconComponent(appDetail.icon) || Brain,
fullDescription: appDetail.description || "無描述",
features: [],
author: appDetail.creator_name || "未知作者",
category: appDetail.category || "未分類",
tags: [],
demoUrl: "",
sourceUrl: "",
likes: appDetail.likes_count || 0,
views: appDetail.views_count || 0,
rating: Number(appDetail.rating) || 0
};
}
return {
id: appId,
name: "未命名應用",
type: "未知類型",
description: "無描述",
icon: Brain,
fullDescription: "無描述",
features: [],
author: "未知作者",
category: "未分類",
tags: [],
demoUrl: "",
sourceUrl: "",
likes: 0,
views: 0,
rating: 0
};
}
const getTypeColor = (type: string) => {
const colors = {
: "bg-blue-100 text-blue-800 border-blue-200",
: "bg-purple-100 text-purple-800 border-purple-200",
: "bg-green-100 text-green-800 border-green-200",
: "bg-orange-100 text-orange-800 border-orange-200",
}
return colors[type as keyof typeof colors] || "bg-gray-100 text-gray-800 border-gray-200"
}
export function TeamDetailDialog({ open, onOpenChange, team }: TeamDetailDialogProps) {
const { getLikeCount, getViewCount, getAppRating } = useAuth()
const [selectedTab, setSelectedTab] = useState("overview")
const [selectedApp, setSelectedApp] = useState<any>(null)
const [appDetailOpen, setAppDetailOpen] = useState(false)
if (!team) return null
const leader = team.members.find((m: any) => m.user_id === team.leader)
const handleAppClick = (appId: string) => {
const appDetails = getAppDetails(appId, team)
// Create app object that matches AppDetailDialog interface
const app = {
id: Number.parseInt(appId),
name: appDetails.name,
type: appDetails.type,
department: team.department, // Use team's department
description: appDetails.description,
icon: appDetails.icon,
creator: appDetails.author,
featured: false,
judgeScore: appDetails.rating || 0,
likes: appDetails.likes || 0,
views: appDetails.views || 0,
}
setSelectedApp(app)
setAppDetailOpen(true)
}
// 使用從數據庫獲取的真實數據
const totalLikes = team.totalLikes || 0
const totalViews = team.totalViews || 0
return (
<>
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-4xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="flex items-center space-x-2">
<Users className="w-5 h-5 text-green-500" />
<span>{team.name}</span>
</DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<div className="space-y-6">
{/* Team Overview */}
<Card>
<CardHeader>
<CardTitle className="text-lg"></CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div className="text-center p-4 bg-green-50 rounded-lg">
<div className="text-2xl font-bold text-green-600">{team.members.length}</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="text-center p-4 bg-blue-50 rounded-lg">
<div className="text-2xl font-bold text-blue-600">{team.apps.length}</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="text-center p-4 bg-red-50 rounded-lg">
<div className="text-2xl font-bold text-red-600">{totalLikes}</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="text-center p-4 bg-purple-50 rounded-lg">
<div className="text-2xl font-bold text-purple-600">{totalViews}</div>
<div className="text-sm text-gray-600"></div>
</div>
</div>
</CardContent>
</Card>
{/* Tab Navigation */}
<div className="flex space-x-1 bg-gray-100 p-1 rounded-lg">
<Button
variant={selectedTab === "overview" ? "default" : "ghost"}
size="sm"
onClick={() => setSelectedTab("overview")}
className="flex-1"
>
</Button>
<Button
variant={selectedTab === "members" ? "default" : "ghost"}
size="sm"
onClick={() => setSelectedTab("members")}
className="flex-1"
>
</Button>
<Button
variant={selectedTab === "apps" ? "default" : "ghost"}
size="sm"
onClick={() => setSelectedTab("apps")}
className="flex-1"
>
</Button>
</div>
{/* Tab Content */}
{selectedTab === "overview" && (
<div className="space-y-4">
<Card>
<CardHeader>
<CardTitle className="text-lg"></CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="text-sm font-medium text-gray-700"></label>
<p className="text-gray-900">{team.name}</p>
</div>
<div>
<label className="text-sm font-medium text-gray-700"></label>
<Badge variant="outline" className="ml-2">
{team.department}
</Badge>
</div>
<div>
<label className="text-sm font-medium text-gray-700"></label>
<p className="text-gray-900">{leader?.name || '未指定'}</p>
</div>
<div>
<label className="text-sm font-medium text-gray-700"></label>
<div className="flex items-center space-x-2">
<Mail className="w-4 h-4 text-gray-500" />
<p className="text-gray-900">{team.contact_email || '未提供'}</p>
</div>
</div>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-lg"></CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="text-center p-4 border rounded-lg">
<Trophy className="w-8 h-8 text-yellow-500 mx-auto mb-2" />
<div className="text-lg font-bold">{team.popularityScore}</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="text-center p-4 border rounded-lg">
<Eye className="w-8 h-8 text-blue-500 mx-auto mb-2" />
<div className="text-lg font-bold">{totalViews}</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="text-center p-4 border rounded-lg">
<Heart className="w-8 h-8 text-red-500 mx-auto mb-2" />
<div className="text-lg font-bold">{totalLikes}</div>
<div className="text-sm text-gray-600"></div>
</div>
</div>
</CardContent>
</Card>
</div>
)}
{selectedTab === "members" && (
<Card>
<CardHeader>
<CardTitle className="text-lg"> ({team.members.length})</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{team.members.map((member: any, index: number) => (
<div key={member.id} className="flex items-center space-x-3 p-4 border rounded-lg">
<Avatar className="w-12 h-12">
<AvatarImage src={`/placeholder-40x40.png`} />
<AvatarFallback className="bg-green-100 text-green-700 font-medium">
{member.name[0]}
</AvatarFallback>
</Avatar>
<div className="flex-1">
<div className="flex items-center space-x-2">
<h4 className="font-medium">{member.name}</h4>
{member.id === team.leader && (
<Badge variant="default" className="bg-yellow-100 text-yellow-800 text-xs">
</Badge>
)}
</div>
<p className="text-sm text-gray-600">{member.role}</p>
<p className="text-xs text-gray-500">{member.department}</p>
</div>
</div>
))}
</div>
</CardContent>
</Card>
)}
{selectedTab === "apps" && (
<Card>
<CardHeader>
<CardTitle className="text-lg"> ({team.apps.length})</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{team.apps.map((appId: string) => {
const app = getAppDetails(appId, team)
// 如果沒有圖標,使用默認的 Brain 圖標
const IconComponent = app.icon || Brain
const likes = app.likes || 0
const views = app.views || 0
const rating = app.rating || 0
return (
<Card
key={appId}
className="hover:shadow-md transition-all duration-200 cursor-pointer group"
onClick={() => handleAppClick(appId)}
>
<CardContent className="p-4 flex flex-col h-full">
<div className="flex items-start space-x-3 mb-3">
<div className="w-10 h-10 bg-gray-100 rounded-lg flex items-center justify-center group-hover:bg-blue-100 transition-colors">
<IconComponent className="w-5 h-5 text-gray-600 group-hover:text-blue-600" />
</div>
<div className="flex-1">
<div className="flex items-center space-x-2">
<h4 className="font-medium text-gray-900 group-hover:text-blue-600 transition-colors">
{app.name}
</h4>
<ExternalLink className="w-3 h-3 text-gray-400 group-hover:text-blue-500 opacity-0 group-hover:opacity-100 transition-all" />
</div>
<Badge variant="outline" className={`${getTypeColor(app.type)} text-xs mt-1`}>
{app.type}
</Badge>
</div>
</div>
<p className="text-sm text-gray-600 mb-3 line-clamp-2">{app.description}</p>
<div className="flex items-center justify-between mt-auto">
<div className="flex items-center space-x-3 text-xs text-gray-500">
<div className="flex items-center space-x-1">
<Eye className="w-3 h-3" />
<span>{views}</span>
</div>
<div className="flex items-center space-x-1">
<Star className="w-3 h-3 text-yellow-500" />
<span>{Number(rating).toFixed(1)}</span>
</div>
</div>
<div onClick={(e) => e.stopPropagation()}>
<LikeButton
appId={appId}
size="sm"
likeCount={likes}
showCount={true}
/>
</div>
</div>
</CardContent>
</Card>
)
})}
</div>
</CardContent>
</Card>
)}
</div>
</DialogContent>
</Dialog>
{/* App Detail Dialog */}
{selectedApp && <AppDetailDialog open={appDetailOpen} onOpenChange={setAppDetailOpen} app={selectedApp} />}
</>
)
}