feat: implement full admin panel with dashboard, user management, and platform settings
Adds complete super_admin panel with 9 pages and account owner categories page. Backend includes 5 new DB tables, ~25 API endpoints, settings manager with in-memory cache, and 29 integration tests. Frontend includes reusable admin components (DataTable, Pagination, ActionMenu, etc.) with code-split lazy loading. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
81
frontend/src/components/admin/Pagination.tsx
Normal file
81
frontend/src/components/admin/Pagination.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
interface PaginationProps {
|
||||
page: number
|
||||
totalPages: number
|
||||
total: number
|
||||
pageSize: number
|
||||
onPageChange: (page: number) => void
|
||||
}
|
||||
|
||||
export function Pagination({ page, totalPages, total, pageSize, onPageChange }: PaginationProps) {
|
||||
const start = (page - 1) * pageSize + 1
|
||||
const end = Math.min(page * pageSize, total)
|
||||
|
||||
const getPageNumbers = (): (number | 'ellipsis')[] => {
|
||||
if (totalPages <= 7) {
|
||||
return Array.from({ length: totalPages }, (_, i) => i + 1)
|
||||
}
|
||||
const pages: (number | 'ellipsis')[] = [1]
|
||||
if (page > 3) pages.push('ellipsis')
|
||||
for (let i = Math.max(2, page - 1); i <= Math.min(totalPages - 1, page + 1); i++) {
|
||||
pages.push(i)
|
||||
}
|
||||
if (page < totalPages - 2) pages.push('ellipsis')
|
||||
pages.push(totalPages)
|
||||
return pages
|
||||
}
|
||||
|
||||
if (totalPages <= 1) return null
|
||||
|
||||
const btnBase = cn(
|
||||
'inline-flex h-8 min-w-8 items-center justify-center rounded-md text-sm font-medium',
|
||||
'transition-colors disabled:opacity-50 disabled:pointer-events-none'
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-4 pt-4">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Showing {start}-{end} of {total}
|
||||
</span>
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => onPageChange(page - 1)}
|
||||
disabled={page <= 1}
|
||||
className={cn(btnBase, 'px-2 hover:bg-accent')}
|
||||
>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</button>
|
||||
{getPageNumbers().map((p, i) =>
|
||||
p === 'ellipsis' ? (
|
||||
<span key={`e${i}`} className="px-1 text-muted-foreground">...</span>
|
||||
) : (
|
||||
<button
|
||||
key={p}
|
||||
onClick={() => onPageChange(p)}
|
||||
className={cn(
|
||||
btnBase,
|
||||
'px-2',
|
||||
p === page
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'hover:bg-accent text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{p}
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
<button
|
||||
onClick={() => onPageChange(page + 1)}
|
||||
disabled={page >= totalPages}
|
||||
className={cn(btnBase, 'px-2 hover:bg-accent')}
|
||||
>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Pagination
|
||||
Reference in New Issue
Block a user