diff --git a/src/features/admin/pages/UsersPage.client.js b/src/features/admin/pages/UsersPage.client.js index 7ae3dd8..59c9254 100644 --- a/src/features/admin/pages/UsersPage.client.js +++ b/src/features/admin/pages/UsersPage.client.js @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; -import { Card, Table, StatusBadge, Pagination, Button } from '@zen/core/shared/components'; +import { Card, Table, StatusBadge, Button } from '@zen/core/shared/components'; import { PencilEdit01Icon } from '@zen/core/shared/icons'; import { useToast } from '@zen/core/toast'; @@ -185,18 +185,12 @@ const UsersPageClient = () => { onSort={handleSort} emptyMessage="Aucun utilisateur trouvé" emptyDescription="La base de données est vide" - /> - - diff --git a/src/shared/components/Badge.js b/src/shared/components/Badge.js index 8efb099..5975dc3 100644 --- a/src/shared/components/Badge.js +++ b/src/shared/components/Badge.js @@ -24,9 +24,9 @@ const Badge = ({ }; const sizes = { - sm: 'px-2 py-0.5 rounded text-[12px]', - md: 'px-2.5 py-0.5 rounded text-[12px]', - lg: 'px-3 py-1 rounded text-xs' + sm: 'px-2 py-0.5 rounded-lg text-[12px]', + md: 'px-2.5 py-0.5 rounded-lg text-[12px]', + lg: 'px-3 py-1 rounded-lg text-xs' }; return ( diff --git a/src/shared/components/Button.js b/src/shared/components/Button.js index e6b8744..7258dfc 100644 --- a/src/shared/components/Button.js +++ b/src/shared/components/Button.js @@ -15,7 +15,7 @@ const Button = ({ className = '', ...props }) => { - const baseClassName = 'cursor-pointer inline-flex items-center justify-center font-medium rounded-lgtransition-all duration-[120ms] ease-out focus:outline-none focus:ring-1 disabled:opacity-50 disabled:cursor-not-allowed'; + const baseClassName = 'cursor-pointer inline-flex items-center justify-center font-medium rounded-lg transition-all duration-[120ms] ease-out focus:outline-none focus:ring-1 disabled:opacity-50 disabled:cursor-not-allowed'; const variants = { primary: 'bg-neutral-900 text-white hover:bg-neutral-800 focus:ring-neutral-900/20 dark:bg-white dark:text-black dark:hover:bg-neutral-100 dark:focus:ring-white/20', diff --git a/src/shared/components/Table.js b/src/shared/components/Table.js index 6ee0911..8cf12e0 100644 --- a/src/shared/components/Table.js +++ b/src/shared/components/Table.js @@ -2,6 +2,7 @@ import React from 'react'; import Badge from './Badge'; +import Button from './Button'; import { TorriGateIcon } from '../icons/index.js'; const ROW_SIZE = { @@ -23,18 +24,26 @@ const Table = ({ 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 ( - @@ -43,9 +52,9 @@ const Table = ({ ); }; - const Skeleton = ({ className = "", width = "100%", height = "h-4" }) => ( -
( +
); @@ -57,22 +66,11 @@ const Table = ({ {column.skeleton ? ( column.skeleton.secondary ? (
- - + +
) : ( - + ) ) : ( @@ -95,24 +93,11 @@ const Table = ({ ); const renderCellContent = (item, column) => { - if (column.render) { - return column.render(item); - } - + 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)}`; - } - + 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 || '-'; }; @@ -128,9 +113,7 @@ const Table = ({
{columns.slice(2, 4).map((column) => ( -
- {renderCellContent(item, column)} -
+
{renderCellContent(item, column)}
))}
@@ -154,14 +137,131 @@ const Table = ({
- - + +
); + 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 */} @@ -187,9 +287,7 @@ const Table = ({ {loading ? ( - Array.from({ length: 5 }).map((_, index) => ( - - )) + Array.from({ length: 5 }).map((_, index) => ) ) : data.length === 0 ? ( ) : ( @@ -218,15 +316,11 @@ const Table = ({ {/* Mobile Cards */}
{loading ? ( - Array.from({ length: 3 }).map((_, index) => ( - - )) + Array.from({ length: 3 }).map((_, index) => ) ) : data.length === 0 ? (
- - - +

{emptyMessage}

{emptyDescription}

@@ -247,8 +341,10 @@ const Table = ({ }) )}
+ + {showPagination && }
); }; -export default Table; \ No newline at end of file +export default Table; diff --git a/src/shared/components/index.js b/src/shared/components/index.js index 6cf585c..39f170c 100644 --- a/src/shared/components/index.js +++ b/src/shared/components/index.js @@ -16,7 +16,6 @@ export { } from './LoadingState'; export { default as Loading } from './Loading'; export { default as Modal } from './Modal'; -export { default as Pagination } from './Pagination'; export { default as Select } from './Select'; export { default as StatCard } from './StatCard'; export { default as Table } from './Table';