* feat: reorganize admin panel around accounts * feat: expand admin customer account controls * feat: add admin account detail management * fix: remove unused admin account icon import * refactor: design critique fixes for account pages - Admin accounts: replace dense card grid with compact DataTable - Account settings: remove redundant hero card, stat grid, header pills - Fix bg-accent (orange) misuse on decorative elements across 7 files - Add ConfirmButton for destructive actions (deactivate, remove member) - Replace single-field modals with inline editing (plan, trial) - Add contextual help: display code tooltip, improved empty states - Non-owner aside explanation for hidden owner-only sections - Admin sidebar: group 11 items into 5 labeled sections - Rename UsersPage.tsx → AccountsPage.tsx to match route - Fix border radius consistency, hide zero-count badges Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use get_admin_db for all new admin account endpoints All admin endpoints query across tenants without a tenant context. get_db (app-role, subject to RLS) was never imported and would crash at runtime — replace all 6 occurrences with get_admin_db (BYPASSRLS). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
82 lines
2.5 KiB
TypeScript
82 lines
2.5 KiB
TypeScript
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 text-muted-foreground hover:bg-elevated hover:text-foreground')}
|
|
>
|
|
<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-white'
|
|
: 'text-muted-foreground hover:bg-elevated hover:text-foreground'
|
|
)}
|
|
>
|
|
{p}
|
|
</button>
|
|
)
|
|
)}
|
|
<button
|
|
onClick={() => onPageChange(page + 1)}
|
|
disabled={page >= totalPages}
|
|
className={cn(btnBase, 'px-2 text-muted-foreground hover:bg-elevated hover:text-foreground')}
|
|
>
|
|
<ChevronRight className="h-4 w-4" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default Pagination
|