oxios 1.10.1

Oxios Agent OS — Agent Operating System powered by oxi-sdk
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { Button } from '@/components/ui/button'
import { Select } from '@/components/ui/select'

interface PaginationProps {
  page: number
  limit: number
  total: number
  onPageChange: (page: number) => void
  onLimitChange?: (limit: number) => void
  /** Maximum page buttons to show */
  maxButtons?: number
}

export function Pagination({
  page,
  limit,
  total,
  onPageChange,
  onLimitChange,
  maxButtons = 5,
}: PaginationProps) {
  const { t } = useTranslation()

  const totalPages = Math.max(1, Math.ceil(total / limit))
  const start = (page - 1) * limit + 1
  const end = Math.min(page * limit, total)

  // Build page number buttons
  const pages: (number | '...')[] = []
  if (totalPages <= maxButtons) {
    for (let i = 1; i <= totalPages; i++) pages.push(i)
  } else {
    // Always include first, last
    // Near current page: show up to 2 on each side
    pages.push(1)
    if (page > 3) pages.push('...')
    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('...')
    pages.push(totalPages)
  }

  const go = (p: number) => {
    if (p < 1 || p > totalPages || p === page) return
    onPageChange(p)
  }

  return (
    <div className="flex items-center justify-between px-2 py-2 text-sm text-muted-foreground">
      <div className="flex items-center gap-2">
        {onLimitChange && (
          <Select
            value={String(limit)}
            onValueChange={(v) => onLimitChange(Number(v))}
            placeholder={String(limit)}
            options={[10, 20, 50, 100].map((n) => ({ label: `${n} / page`, value: String(n) }))}
            className="h-8 w-24"
          />
        )}
        <span>
          {t('dataTable.showing', 'Showing')} {start}–{end} {t('dataTable.of', 'of')} {total}
        </span>
      </div>

      <div className="flex items-center gap-1">
        <Button
          variant="ghost"
          size="icon"
          className="h-8 w-8"
          onClick={() => go(1)}
          disabled={page <= 1}
          aria-label={t('common.firstPage', 'First page')}
        >
          <ChevronsLeft className="h-4 w-4" />
        </Button>
        <Button
          variant="ghost"
          size="icon"
          className="h-8 w-8"
          onClick={() => go(page - 1)}
          disabled={page <= 1}
          aria-label={t('common.previousPage', 'Previous page')}
        >
          <ChevronLeft className="h-4 w-4" />
        </Button>

        {pages.map((p, i) =>
          p === '...' ? (
            <span key={`ellipsis-${i}`} className="px-1">
              …
            </span>
          ) : (
            <Button
              key={p}
              variant={p === page ? 'default' : 'ghost'}
              size="icon"
              className="h-8 w-8"
              onClick={() => go(p as number)}
            >
              {p}
            </Button>
          ),
        )}

        <Button
          variant="ghost"
          size="icon"
          className="h-8 w-8"
          onClick={() => go(page + 1)}
          disabled={page >= totalPages}
          aria-label={t('common.nextPage', 'Next page')}
        >
          <ChevronRight className="h-4 w-4" />
        </Button>
        <Button
          variant="ghost"
          size="icon"
          className="h-8 w-8"
          onClick={() => go(totalPages)}
          disabled={page >= totalPages}
          aria-label={t('common.lastPage', 'Last page')}
        >
          <ChevronsRight className="h-4 w-4" />
        </Button>
      </div>
    </div>
  )
}