- Backend: Node.js + Express + TypeScript + Prisma + PostgreSQL - Frontend: React + TypeScript + Tailwind CSS + Vite - Multi-tenant with role-based access (SystemAdmin, Technician, Customer) - Workflow editor with drag-and-drop steps - JWT authentication with refresh tokens - German UI throughout
63 lines
1.7 KiB
TypeScript
63 lines
1.7 KiB
TypeScript
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
|
|
|
interface PaginationProps {
|
|
page: number;
|
|
totalPages: number;
|
|
onPageChange: (page: number) => void;
|
|
}
|
|
|
|
export default function Pagination({
|
|
page,
|
|
totalPages,
|
|
onPageChange,
|
|
}: PaginationProps) {
|
|
if (totalPages <= 1) return null;
|
|
|
|
const pages: (number | '...')[] = [];
|
|
for (let i = 1; i <= totalPages; i++) {
|
|
if (i === 1 || i === totalPages || (i >= page - 1 && i <= page + 1)) {
|
|
pages.push(i);
|
|
} else if (pages[pages.length - 1] !== '...') {
|
|
pages.push('...');
|
|
}
|
|
}
|
|
|
|
return (
|
|
<nav className="flex items-center justify-center gap-1">
|
|
<button
|
|
onClick={() => onPageChange(page - 1)}
|
|
disabled={page <= 1}
|
|
className="rounded-lg p-2 text-gray-500 hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-40"
|
|
>
|
|
<ChevronLeft size={18} />
|
|
</button>
|
|
{pages.map((p, idx) =>
|
|
p === '...' ? (
|
|
<span key={`dots-${idx}`} className="px-2 text-gray-400">
|
|
...
|
|
</span>
|
|
) : (
|
|
<button
|
|
key={p}
|
|
onClick={() => onPageChange(p)}
|
|
className={`min-w-[36px] rounded-lg px-3 py-1.5 text-sm font-medium ${
|
|
p === page
|
|
? 'bg-primary-600 text-white'
|
|
: 'text-gray-600 hover:bg-gray-100'
|
|
}`}
|
|
>
|
|
{p}
|
|
</button>
|
|
)
|
|
)}
|
|
<button
|
|
onClick={() => onPageChange(page + 1)}
|
|
disabled={page >= totalPages}
|
|
className="rounded-lg p-2 text-gray-500 hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-40"
|
|
>
|
|
<ChevronRight size={18} />
|
|
</button>
|
|
</nav>
|
|
);
|
|
}
|