"use client" import { useState, useEffect } from "react" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Badge } from "@/components/ui/badge" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Label } from "@/components/ui/label" import { Textarea } from "@/components/ui/textarea" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" import { Search, Plus, MoreHorizontal, Edit, Trash2, Eye, Star, Heart, TrendingUp, Bot, CheckCircle, Clock, MessageSquare, ExternalLink, AlertTriangle, X, Check, TrendingDown, Link, Zap, Brain, Mic, ImageIcon, FileText, BarChart3, Camera, Music, Video, Code, Database, Globe, Smartphone, Monitor, Headphones, Palette, Calculator, Shield, Settings, Lightbulb, } from "lucide-react" // Add available icons array after imports const availableIcons = [ { name: "Bot", icon: Bot, color: "from-blue-500 to-purple-500" }, { name: "Brain", icon: Brain, color: "from-purple-500 to-pink-500" }, { name: "Zap", icon: Zap, color: "from-yellow-500 to-orange-500" }, { name: "Mic", icon: Mic, color: "from-green-500 to-teal-500" }, { name: "ImageIcon", icon: ImageIcon, color: "from-pink-500 to-rose-500" }, { name: "FileText", icon: FileText, color: "from-blue-500 to-cyan-500" }, { name: "BarChart3", icon: BarChart3, color: "from-emerald-500 to-green-500" }, { name: "Camera", icon: Camera, color: "from-indigo-500 to-purple-500" }, { name: "Music", icon: Music, color: "from-violet-500 to-purple-500" }, { name: "Video", icon: Video, color: "from-red-500 to-pink-500" }, { name: "Code", icon: Code, color: "from-gray-500 to-slate-500" }, { name: "Database", icon: Database, color: "from-cyan-500 to-blue-500" }, { name: "Globe", icon: Globe, color: "from-blue-500 to-indigo-500" }, { name: "Smartphone", icon: Smartphone, color: "from-slate-500 to-gray-500" }, { name: "Monitor", icon: Monitor, color: "from-gray-600 to-slate-600" }, { name: "Headphones", icon: Headphones, color: "from-purple-500 to-violet-500" }, { name: "Palette", icon: Palette, color: "from-pink-500 to-purple-500" }, { name: "Calculator", icon: Calculator, color: "from-orange-500 to-red-500" }, { name: "Shield", icon: Shield, color: "from-green-500 to-emerald-500" }, { name: "Settings", icon: Settings, color: "from-gray-500 to-zinc-500" }, { name: "Lightbulb", icon: Lightbulb, color: "from-yellow-500 to-amber-500" }, ] // App data - empty for production const mockApps: any[] = [] export function AppManagement() { const [apps, setApps] = useState(mockApps) const [loading, setLoading] = useState(true) const [searchTerm, setSearchTerm] = useState("") const [selectedType, setSelectedType] = useState("all") const [selectedStatus, setSelectedStatus] = useState("all") const [selectedApp, setSelectedApp] = useState(null) const [showAppDetail, setShowAppDetail] = useState(false) const [showAddApp, setShowAddApp] = useState(false) const [showEditApp, setShowEditApp] = useState(false) const [showDeleteConfirm, setShowDeleteConfirm] = useState(false) const [showApprovalDialog, setShowApprovalDialog] = useState(false) const [approvalAction, setApprovalAction] = useState<"approve" | "reject">("approve") const [approvalReason, setApprovalReason] = useState("") const [currentPage, setCurrentPage] = useState(1) const [totalPages, setTotalPages] = useState(1) const [totalApps, setTotalApps] = useState(0) const [stats, setStats] = useState({ published: 0, pending: 0, draft: 0, rejected: 0 }) const itemsPerPage = 10 const [newApp, setNewApp] = useState({ name: "", type: "文字處理", department: "HQBU", creator: "", description: "", appUrl: "", icon: "Bot", iconColor: "from-blue-500 to-purple-500", }) // 載入應用程式 useEffect(() => { const loadApps = async () => { try { setLoading(true) const token = localStorage.getItem('token') if (!token) { console.log('未找到 token,跳過載入應用程式') setLoading(false) return } const params = new URLSearchParams({ page: currentPage.toString(), limit: itemsPerPage.toString() }) if (searchTerm) { params.append('search', searchTerm) } if (selectedType !== 'all') { params.append('type', mapTypeToApiType(selectedType)) } if (selectedStatus !== 'all') { params.append('status', selectedStatus) } const response = await fetch(`/api/apps?${params.toString()}`, { method: 'GET', headers: { 'Authorization': `Bearer ${token}` } }) if (!response.ok) { throw new Error(`載入應用程式失敗: ${response.status}`) } const data = await response.json() console.log('載入的應用程式:', data) // 轉換 API 資料格式為前端期望的格式 const formattedApps = (data.apps || []).map((app: any) => ({ ...app, creator: app.creator?.name || '未知', department: app.creator?.department || '未知', views: app.viewsCount || 0, likes: app.likesCount || 0, appUrl: app.demoUrl || '', type: mapApiTypeToDisplayType(app.type), // 將 API 類型轉換為中文顯示 icon: 'Bot', iconColor: 'from-blue-500 to-purple-500', reviews: 0, // API 中沒有評論數,設為 0 createdAt: app.createdAt ? new Date(app.createdAt).toLocaleDateString() : '未知' })) console.log('格式化後的應用程式:', formattedApps) setApps(formattedApps) // 更新分頁資訊和統計 if (data.pagination) { setTotalPages(data.pagination.totalPages) setTotalApps(data.pagination.total) } if (data.stats) { setStats(data.stats) } } catch (error) { console.error('載入應用程式失敗:', error) } finally { setLoading(false) } } loadApps() }, [currentPage, searchTerm, selectedType, selectedStatus]) // 當過濾條件改變時,重置到第一頁 useEffect(() => { setCurrentPage(1) }, [searchTerm, selectedType, selectedStatus]) // 使用從 API 返回的應用程式,因為過濾已在服務器端完成 const filteredApps = apps const handleViewApp = (app: any) => { setSelectedApp(app) setShowAppDetail(true) } const handleEditApp = (app: any) => { setSelectedApp(app) setNewApp({ name: app.name, type: app.type, department: app.department, creator: app.creator, description: app.description, appUrl: app.appUrl, icon: app.icon || "Bot", iconColor: app.iconColor || "from-blue-500 to-purple-500", }) setShowEditApp(true) } const handleDeleteApp = (app: any) => { setSelectedApp(app) setShowDeleteConfirm(true) } const confirmDeleteApp = () => { if (selectedApp) { setApps(apps.filter((app) => app.id !== selectedApp.id)) setShowDeleteConfirm(false) setSelectedApp(null) } } const handleToggleAppStatus = (appId: string) => { setApps( apps.map((app) => app.id === appId ? { ...app, status: app.status === "published" ? "draft" : "published", } : app, ), ) } const handleApprovalAction = (app: any, action: "approve" | "reject") => { setSelectedApp(app) setApprovalAction(action) setApprovalReason("") setShowApprovalDialog(true) } const confirmApproval = async () => { if (selectedApp) { try { const token = localStorage.getItem('token') if (!token) { throw new Error('未找到認證 token,請重新登入') } // 準備更新資料 const updateData = { status: approvalAction === "approve" ? "published" : "rejected" } // 如果有備註或原因,可以添加到描述中或創建一個新的欄位 if (approvalReason.trim()) { // 這裡可以根據需要添加備註欄位 console.log(`${approvalAction === "approve" ? "批准備註" : "拒絕原因"}:`, approvalReason) } const response = await fetch(`/api/apps/${selectedApp.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(updateData) }) if (!response.ok) { const errorData = await response.json() throw new Error(errorData.error || `更新失敗: ${response.status}`) } // 更新本地狀態 setApps( apps.map((app) => app.id === selectedApp.id ? { ...app, status: approvalAction === "approve" ? "published" : "rejected", } : app, ), ) // 顯示成功訊息 alert(`應用程式已${approvalAction === "approve" ? "批准" : "拒絕"}`) setShowApprovalDialog(false) setSelectedApp(null) setApprovalReason("") } catch (error) { console.error('更新應用程式狀態失敗:', error) const errorMessage = error instanceof Error ? error.message : '未知錯誤' alert(`更新失敗: ${errorMessage}`) } } } const handleAddApp = async () => { try { // 準備應用程式資料 const appData = { name: newApp.name, description: newApp.description, type: mapTypeToApiType(newApp.type), demoUrl: newApp.appUrl || undefined, version: '1.0.0' } console.log('準備提交的應用資料:', appData) // 調用 API 創建應用程式 const token = localStorage.getItem('token') console.log('Token:', token ? '存在' : '不存在') if (!token) { throw new Error('未找到認證 token,請重新登入') } const response = await fetch('/api/apps', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(appData) }) console.log('API 回應狀態:', response.status, response.statusText) if (!response.ok) { const errorData = await response.json() console.error('API 錯誤詳情:', errorData) throw new Error(errorData.error || `API 錯誤: ${response.status} ${response.statusText}`) } const result = await response.json() console.log('應用程式創建成功:', result) // 更新本地狀態 const app = { id: result.appId || Date.now().toString(), ...newApp, status: result.app?.status || "draft", // 使用 API 返回的狀態 createdAt: new Date().toISOString().split("T")[0], views: 0, likes: 0, rating: 0, reviews: 0, } setApps([...apps, app]) setNewApp({ name: "", type: "文字處理", department: "HQBU", creator: "", description: "", appUrl: "", icon: "Bot", iconColor: "from-blue-500 to-purple-500", }) setShowAddApp(false) } catch (error) { console.error('創建應用程式失敗:', error) const errorMessage = error instanceof Error ? error.message : '未知錯誤' alert(`創建應用程式失敗: ${errorMessage}`) } } // 將前端類型映射到 API 類型 const mapTypeToApiType = (frontendType: string): string => { const typeMap: Record = { '文字處理': 'productivity', '圖像生成': 'ai_model', '圖像處理': 'ai_model', '語音辨識': 'ai_model', '推薦系統': 'ai_model', '音樂生成': 'ai_model', '程式開發': 'automation', '影像處理': 'ai_model', '對話系統': 'ai_model', '數據分析': 'data_analysis', '設計工具': 'productivity', '語音技術': 'ai_model', '教育工具': 'educational', '健康醫療': 'healthcare', '金融科技': 'finance', '物聯網': 'iot_device', '區塊鏈': 'blockchain', 'AR/VR': 'ar_vr', '機器學習': 'machine_learning', '電腦視覺': 'computer_vision', '自然語言處理': 'nlp', '機器人': 'robotics', '網路安全': 'cybersecurity', '雲端服務': 'cloud_service', '其他': 'other' } return typeMap[frontendType] || 'other' } // 將 API 類型映射到前端顯示的中文類型 const mapApiTypeToDisplayType = (apiType: string): string => { const typeMap: Record = { 'productivity': '文字處理', 'ai_model': '圖像生成', 'automation': '程式開發', 'data_analysis': '數據分析', 'educational': '教育工具', 'healthcare': '健康醫療', 'finance': '金融科技', 'iot_device': '物聯網', 'blockchain': '區塊鏈', 'ar_vr': 'AR/VR', 'machine_learning': '機器學習', 'computer_vision': '電腦視覺', 'nlp': '自然語言處理', 'robotics': '機器人', 'cybersecurity': '網路安全', 'cloud_service': '雲端服務', 'other': '其他' } return typeMap[apiType] || '其他' } const handleUpdateApp = () => { if (selectedApp) { setApps( apps.map((app) => app.id === selectedApp.id ? { ...app, ...newApp, } : app, ), ) setShowEditApp(false) setSelectedApp(null) } } const getStatusColor = (status: string) => { switch (status) { case "published": return "bg-green-100 text-green-800 border-green-200" case "pending": return "bg-yellow-100 text-yellow-800 border-yellow-200" case "draft": return "bg-gray-100 text-gray-800 border-gray-200" case "rejected": return "bg-red-100 text-red-800 border-red-200" default: return "bg-gray-100 text-gray-800 border-gray-200" } } 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-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", 音樂生成: "bg-pink-100 text-pink-800 border-pink-200", 程式開發: "bg-indigo-100 text-indigo-800 border-indigo-200", 影像處理: "bg-purple-100 text-purple-800 border-purple-200", 對話系統: "bg-teal-100 text-teal-800 border-teal-200", 數據分析: "bg-cyan-100 text-cyan-800 border-cyan-200", 設計工具: "bg-blue-100 text-blue-800 border-blue-200", 語音技術: "bg-green-100 text-green-800 border-green-200", 教育工具: "bg-emerald-100 text-emerald-800 border-emerald-200", 健康醫療: "bg-red-100 text-red-800 border-red-200", 金融科技: "bg-yellow-100 text-yellow-800 border-yellow-200", 物聯網: "bg-slate-100 text-slate-800 border-slate-200", 區塊鏈: "bg-violet-100 text-violet-800 border-violet-200", 'AR/VR': "bg-fuchsia-100 text-fuchsia-800 border-fuchsia-200", 機器學習: "bg-rose-100 text-rose-800 border-rose-200", 電腦視覺: "bg-purple-100 text-purple-800 border-purple-200", 自然語言處理: "bg-teal-100 text-teal-800 border-teal-200", 機器人: "bg-gray-100 text-gray-800 border-gray-200", 網路安全: "bg-red-100 text-red-800 border-red-200", 雲端服務: "bg-sky-100 text-sky-800 border-sky-200", 其他: "bg-gray-100 text-gray-800 border-gray-200" } return colors[type as keyof typeof colors] || "bg-gray-100 text-gray-800 border-gray-200" } const getStatusText = (status: string) => { switch (status) { case "published": return "已發布" case "pending": return "待審核" case "draft": return "草稿" case "rejected": return "已拒絕" default: return status } } return (
{/* Header */}

應用管理

管理平台上的所有 AI 應用

{/* Stats Cards */}

總應用數

{totalApps}

已發布

{stats.published}

待審核

{stats.pending}

{/* Filters */}
setSearchTerm(e.target.value)} className="pl-10" />
{/* Apps Table */} 應用列表 ({totalApps}) 管理所有 AI 應用 應用名稱 類型 創建者 狀態 統計 評分 創建日期 操作 {loading ? (
載入應用程式中...
) : filteredApps.length === 0 ? (

尚無應用程式

點擊右上角的「新增應用」按鈕來創建第一個應用程式

) : ( filteredApps.map((app) => (
{(() => { const IconComponent = availableIcons.find((icon) => icon.name === app.icon)?.icon || Bot return })()}

{app.name}

{app.appUrl && ( )}
{app.type}

{app.creator}

{app.department}

{getStatusText(app.status)}
{app.views}
{app.likes}
{app.rating} ({app.reviews})
{app.createdAt} handleViewApp(app)}> 查看詳情 handleEditApp(app)}> 編輯應用 {app.appUrl && ( window.open(app.appUrl, "_blank")}> 開啟應用 )} {app.status === "pending" && ( <> handleApprovalAction(app, "approve")}> 批准發布 handleApprovalAction(app, "reject")}> 拒絕申請 )} {app.status !== "pending" && ( handleToggleAppStatus(app.id)}> {app.status === "published" ? ( <> 下架應用 ) : ( <> 發布應用 )} )} handleDeleteApp(app)}> 刪除應用
)) )}
{/* Pagination */} {totalPages > 1 && (
顯示第 {((currentPage - 1) * itemsPerPage) + 1} 到 {Math.min(currentPage * itemsPerPage, totalApps)} 筆,共 {totalApps} 筆
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => { const pageNum = i + 1 return ( ) })}
)} {/* Add App Dialog */} 新增 AI 應用 創建一個新的 AI 應用
setNewApp({ ...newApp, name: e.target.value })} placeholder="輸入應用名稱" />
setNewApp({ ...newApp, creator: e.target.value })} placeholder="輸入創建者姓名" />
{availableIcons.map((iconOption) => { const IconComponent = iconOption.icon return ( ) })}

選擇一個代表您應用的圖示

setNewApp({ ...newApp, appUrl: e.target.value })} placeholder="https://your-app.example.com" className="pl-10" />

用戶點擊應用時將跳轉到此連結