新增儀表板快速操作功能

This commit is contained in:
2025-08-05 12:35:41 +08:00
parent 279720e276
commit d0c4adf243
3 changed files with 93 additions and 10 deletions

View File

@@ -15,7 +15,7 @@ export function AdminPanel() {
const renderPage = () => {
switch (currentPage) {
case "dashboard":
return <AdminDashboard />
return <AdminDashboard onPageChange={setCurrentPage} />
case "users":
return <UserManagement />
case "apps":
@@ -27,7 +27,7 @@ export function AdminPanel() {
case "settings":
return <SystemSettings />
default:
return <AdminDashboard />
return <AdminDashboard onPageChange={setCurrentPage} />
}
}

View File

@@ -20,9 +20,19 @@ const recentActivities: any[] = []
const topApps: any[] = []
export function AdminDashboard() {
interface AdminDashboardProps {
onPageChange?: (page: string) => void
}
export function AdminDashboard({ onPageChange }: AdminDashboardProps) {
const { competitions } = useCompetition()
const handleManageUsers = () => {
if (onPageChange) {
onPageChange("users")
}
}
return (
<div className="space-y-6">
{/* Welcome Section */}
@@ -150,7 +160,7 @@ export function AdminDashboard() {
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Button className="h-20 flex flex-col space-y-2">
<Button className="h-20 flex flex-col space-y-2" onClick={handleManageUsers}>
<Users className="w-6 h-6" />
<span></span>
</Button>

View File

@@ -13,6 +13,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import { Label } from "@/components/ui/label"
import { Alert, AlertDescription } from "@/components/ui/alert"
import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "@/components/ui/pagination"
import {
Search,
MoreHorizontal,
@@ -68,14 +69,20 @@ export function UserManagement() {
totalReviews: 0
})
// Pagination state
const [currentPage, setCurrentPage] = useState(1)
const [totalPages, setTotalPages] = useState(1)
const [totalUsers, setTotalUsers] = useState(0)
const [itemsPerPage] = useState(10) // Default to 10 items per page
// 載入用戶資料
useEffect(() => {
const fetchUsers = async () => {
try {
setIsLoading(true)
// 獲取用戶列表
const usersResponse = await fetch('/api/users', {
// 獲取用戶列表 with pagination
const usersResponse = await fetch(`/api/users?page=${currentPage}&limit=${itemsPerPage}`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
@@ -84,6 +91,8 @@ export function UserManagement() {
if (usersResponse.ok) {
const usersData = await usersResponse.json()
setUsers(usersData.users || [])
setTotalPages(usersData.pagination?.totalPages || 1)
setTotalUsers(usersData.pagination?.total || 0)
} else {
const errorData = await usersResponse.json().catch(() => ({}))
console.error('獲取用戶列表失敗:', errorData.error || usersResponse.statusText)
@@ -105,14 +114,15 @@ export function UserManagement() {
console.error('獲取統計資料失敗:', errorData.error || statsResponse.statusText)
}
} catch (error) {
console.error('載入資料失敗:', error)
console.error('載入用戶資料失敗:', error)
setError('載入用戶資料失敗')
} finally {
setIsLoading(false)
}
}
fetchUsers()
}, [])
}, [currentPage, itemsPerPage]) // Re-fetch when page changes
// 重新獲取統計數據的函數
const refreshStats = async () => {
@@ -734,8 +744,11 @@ export function UserManagement() {
{/* Users Table */}
<Card>
<CardHeader>
<CardTitle> ({filteredUsers.length})</CardTitle>
<CardDescription></CardDescription>
<CardTitle> ({totalUsers} )</CardTitle>
<CardDescription>
- {currentPage} {totalPages}
{totalPages > 1 && ` (每頁 ${itemsPerPage} 筆)`}
</CardDescription>
</CardHeader>
<CardContent>
{isLoading ? (
@@ -859,6 +872,66 @@ export function UserManagement() {
</CardContent>
</Card>
{/* Pagination */}
{totalPages > 1 && (
<div className="flex justify-center items-center py-4">
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious
href="#"
onClick={(e) => {
e.preventDefault()
if (currentPage > 1) setCurrentPage(currentPage - 1)
}}
className={currentPage === 1 ? "pointer-events-none opacity-50" : ""}
/>
</PaginationItem>
{/* Page numbers */}
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
let pageNum
if (totalPages <= 5) {
pageNum = i + 1
} else if (currentPage <= 3) {
pageNum = i + 1
} else if (currentPage >= totalPages - 2) {
pageNum = totalPages - 4 + i
} else {
pageNum = currentPage - 2 + i
}
return (
<PaginationItem key={pageNum}>
<PaginationLink
href="#"
onClick={(e) => {
e.preventDefault()
setCurrentPage(pageNum)
}}
isActive={currentPage === pageNum}
>
{pageNum}
</PaginationLink>
</PaginationItem>
)
})}
<PaginationItem>
<PaginationNext
href="#"
onClick={(e) => {
e.preventDefault()
if (currentPage < totalPages) setCurrentPage(currentPage + 1)
}}
className={currentPage === totalPages ? "pointer-events-none opacity-50" : ""}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
</div>
)}
{/* Invite User Dialog - 包含角色選擇 */}
<Dialog open={showInviteUser} onOpenChange={setShowInviteUser}>
<DialogContent className="max-w-md">