fix(erp): enable erp pages and menu items
This commit is contained in:
178
front-end-agency/components/ui/DataTable.tsx
Normal file
178
front-end-agency/components/ui/DataTable.tsx
Normal file
@@ -0,0 +1,178 @@
|
||||
"use client";
|
||||
|
||||
import { ReactNode } from "react";
|
||||
import { Button } from "./index";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
interface Column<T> {
|
||||
header: string;
|
||||
accessor: keyof T | ((item: T) => ReactNode);
|
||||
className?: string;
|
||||
align?: 'left' | 'center' | 'right';
|
||||
}
|
||||
|
||||
interface DataTableProps<T> {
|
||||
columns: Column<T>[];
|
||||
data: T[];
|
||||
isLoading?: boolean;
|
||||
emptyMessage?: string;
|
||||
pagination?: {
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
onPageChange: (page: number) => void;
|
||||
totalItems: number;
|
||||
};
|
||||
onRowClick?: (item: T) => void;
|
||||
selectable?: boolean;
|
||||
selectedIds?: (string | number)[];
|
||||
onSelectionChange?: (ids: (string | number)[]) => void;
|
||||
}
|
||||
|
||||
export default function DataTable<T extends { id: string | number }>({
|
||||
columns,
|
||||
data,
|
||||
isLoading = false,
|
||||
emptyMessage = "Nenhum resultado encontrado.",
|
||||
pagination,
|
||||
onRowClick,
|
||||
selectable = false,
|
||||
selectedIds = [],
|
||||
onSelectionChange
|
||||
}: DataTableProps<T>) {
|
||||
const handleSelectAll = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (!onSelectionChange) return;
|
||||
if (e.target.checked) {
|
||||
onSelectionChange(data.map(item => item.id));
|
||||
} else {
|
||||
onSelectionChange([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectItem = (id: string | number) => {
|
||||
if (!onSelectionChange) return;
|
||||
if (selectedIds.includes(id)) {
|
||||
onSelectionChange(selectedIds.filter(i => i !== id));
|
||||
} else {
|
||||
onSelectionChange([...selectedIds, id]);
|
||||
}
|
||||
};
|
||||
|
||||
const isAllSelected = data.length > 0 && selectedIds.length === data.length;
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-zinc-50/50 dark:bg-zinc-800/50 border-b border-zinc-200 dark:border-zinc-800">
|
||||
{selectable && (
|
||||
<th className="px-6 py-4 w-10 text-left">
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isAllSelected}
|
||||
onChange={handleSelectAll}
|
||||
className="w-4 h-4 rounded border-zinc-300 text-brand-600 focus:ring-brand-500 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
)}
|
||||
{columns.map((column, index) => (
|
||||
<th
|
||||
key={index}
|
||||
className={`px-6 py-4 text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wider ${column.align === 'right' ? 'text-right' :
|
||||
column.align === 'center' ? 'text-center' : 'text-left'
|
||||
} ${column.className || ''}`}
|
||||
>
|
||||
{column.header}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-zinc-100 dark:divide-zinc-800">
|
||||
{isLoading ? (
|
||||
Array.from({ length: 3 }).map((_, i) => (
|
||||
<tr key={i} className="animate-pulse">
|
||||
{columns.map((_, j) => (
|
||||
<td key={j} className="px-6 py-4">
|
||||
<div className="h-4 bg-zinc-100 dark:bg-zinc-800 rounded w-full"></div>
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))
|
||||
) : data.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={columns.length + (selectable ? 1 : 0)} className="px-6 py-12 text-center text-sm text-zinc-500 dark:text-zinc-400">
|
||||
{emptyMessage}
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
data.map((item) => {
|
||||
const isSelected = selectedIds.includes(item.id);
|
||||
return (
|
||||
<tr
|
||||
key={item.id}
|
||||
onClick={() => onRowClick?.(item)}
|
||||
className={`transition-colors group ${isSelected ? 'bg-brand-50/30 dark:bg-brand-500/5' : ''} ${onRowClick ? 'cursor-pointer hover:bg-zinc-50 dark:hover:bg-zinc-800/50' : 'hover:bg-zinc-50/50 dark:hover:bg-zinc-800/30'}`}
|
||||
>
|
||||
{selectable && (
|
||||
<td className="px-6 py-4 w-10" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isSelected}
|
||||
onChange={() => handleSelectItem(item.id)}
|
||||
className="w-4 h-4 rounded border-zinc-300 text-brand-600 focus:ring-brand-500 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
)}
|
||||
{columns.map((column, index) => (
|
||||
<td
|
||||
key={index}
|
||||
className={`px-6 py-4 text-sm text-zinc-600 dark:text-zinc-300 ${column.align === 'right' ? 'text-right' :
|
||||
column.align === 'center' ? 'text-center' : 'text-left'
|
||||
} ${column.className || ''}`}
|
||||
>
|
||||
{typeof column.accessor === 'function'
|
||||
? column.accessor(item)
|
||||
: (item[column.accessor] as ReactNode)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{pagination && (
|
||||
<div className="p-4 bg-zinc-50/30 dark:bg-zinc-900/30 border-t border-zinc-200 dark:border-zinc-800 flex items-center justify-between">
|
||||
<span className="text-xs text-zinc-500 italic">
|
||||
Mostrando {data.length} de {pagination.totalItems} resultados
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={pagination.currentPage <= 1 || isLoading}
|
||||
onClick={() => pagination.onPageChange(pagination.currentPage - 1)}
|
||||
>
|
||||
<ChevronLeftIcon className="w-4 h-4 mr-1" />
|
||||
Anterior
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={pagination.currentPage >= pagination.totalPages || isLoading}
|
||||
onClick={() => pagination.onPageChange(pagination.currentPage + 1)}
|
||||
>
|
||||
Próximo
|
||||
<ChevronRightIcon className="w-4 h-4 ml-1" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user