'use client';
import React from 'react';
import Badge from './Badge';
import Button from './Button';
import { TorriGateIcon } from '../icons/index.js';
const ROW_SIZE = {
sm: { cell: 'px-4 py-[11px]', header: 'px-4 py-[9px]', mobile: 'p-4' },
md: { cell: 'px-4 py-[14px]', header: 'px-4 py-[12px]', mobile: 'p-5' },
lg: { cell: 'px-4 py-[18px]', header: 'px-4 py-[14px]', mobile: 'p-6' },
};
const Table = ({
columns = [],
data = [],
loading = false,
sortBy,
sortOrder,
onSort,
onRowClick,
getRowProps,
emptyMessage = 'Aucune donnée',
emptyDescription = 'Aucun élément à afficher',
size = 'sm',
className = '',
// Pagination props
currentPage = 1,
totalPages = 1,
onPageChange,
onLimitChange,
limit = 20,
total = 0,
...props
}) => {
const sizeClasses = ROW_SIZE[size] ?? ROW_SIZE.md;
const showPagination = total > 20;
const SortIcon = ({ column }) => {
const isActive = sortBy === column.key;
const isDesc = isActive && sortOrder === 'desc';
return (
);
};
const Skeleton = ({ className: cx = '', width = '100%', height = 'h-4' }) => (
);
const SkeletonRow = () => (
{columns.map((column, index) => (
|
{column.skeleton ? (
column.skeleton.secondary ? (
) : (
)
) : (
)}
|
))}
);
const EmptyState = () => (
|
{emptyMessage}
{emptyDescription}
|
);
const renderCellContent = (item, column) => {
if (column.render) return column.render(item);
const value = column.key.split('.').reduce((obj, key) => obj?.[key], item);
if (column.type === 'badge') return {value};
if (column.type === 'date') return new Date(value).toLocaleDateString();
if (column.type === 'currency') return `$${parseFloat(value || 0).toFixed(2)}`;
return value || '-';
};
const MobileCard = ({ item }) => (
{columns.slice(0, 2).map((column) => (
{renderCellContent(item, column)}
))}
{columns.slice(2, 4).map((column) => (
{renderCellContent(item, column)}
))}
{columns.length > 4 && (
{columns.slice(4).map((column) => (
{column.label}: {renderCellContent(item, column)}
))}
)}
);
const MobileSkeletonCard = () => (
);
const getPageNumbers = () => {
const pages = [];
const delta = 2;
if (totalPages > 0) pages.push(1);
if (currentPage - delta > 2) pages.push('...');
for (let i = Math.max(2, currentPage - delta); i <= Math.min(totalPages - 1, currentPage + delta); i++) {
pages.push(i);
}
if (currentPage + delta < totalPages - 1) pages.push('...');
if (totalPages > 1) pages.push(totalPages);
return pages;
};
const PageButton = ({ onClick, disabled, children, isActive = false }) => (
);
const from = total > 0 ? (currentPage - 1) * limit + 1 : 0;
const to = Math.min(currentPage * limit, total);
const PaginationBar = () => (
{loading ? (
) : (
<>
Afficher
>
)}
{loading ? (
<>
{Array.from({ length: 3 }).map((_, i) => (
))}
>
) : (
<>
{totalPages > 1 && (
)}
{totalPages > 1 && (
{getPageNumbers().map((page, index) => (
{page === '...' ? (
…
) : (
onPageChange(page)} isActive={currentPage === page}>
{page}
)}
))}
)}
{totalPages > 1 && (
)}
>
)}
{loading ? (
) : (
`${from}–${to} sur ${total}`
)}
);
return (
{/* Desktop Table */}
{columns.map((column) => (
| onSort(column.key) : undefined}
>
{column.label}
{column.sortable && onSort && }
|
))}
{loading ? (
Array.from({ length: 5 }).map((_, index) => )
) : data.length === 0 ? (
) : (
data.map((item, index) => {
const isLast = index === data.length - 1;
const { className: extraClassName, ...rowExtraProps } = getRowProps ? getRowProps(item) : {};
return (
onRowClick(item) : undefined}
{...rowExtraProps}
>
{columns.map((column, colIdx) => (
|
{renderCellContent(item, column)}
|
))}
);
})
)}
{/* Mobile Cards */}
{loading ? (
Array.from({ length: 3 }).map((_, index) =>
)
) : data.length === 0 ? (
{emptyMessage}
{emptyDescription}
) : (
data.map((item, index) => {
const { className: extraClassName, ...rowExtraProps } = getRowProps ? getRowProps(item) : {};
return (
onRowClick(item) : undefined}
{...rowExtraProps}
>
);
})
)}
{showPagination &&
}
);
};
export default Table;