67 lines
2.0 KiB
TypeScript
67 lines
2.0 KiB
TypeScript
import type { ReactNode } from 'react';
|
|
|
|
import { EmptyState } from './ui/EmptyState';
|
|
|
|
export type TableColumn<TItem> = {
|
|
key: string;
|
|
label: string;
|
|
render: (item: TItem) => ReactNode;
|
|
};
|
|
|
|
type DataTableProps<TItem> = {
|
|
items: TItem[];
|
|
columns: TableColumn<TItem>[];
|
|
getRowKey: (item: TItem) => string;
|
|
actions?: (item: TItem) => ReactNode;
|
|
emptyTitle?: string;
|
|
emptyDescription?: string;
|
|
};
|
|
|
|
export function DataTable<TItem>({
|
|
items,
|
|
columns,
|
|
getRowKey,
|
|
actions,
|
|
emptyTitle,
|
|
emptyDescription,
|
|
}: DataTableProps<TItem>) {
|
|
return (
|
|
<div className="overflow-hidden rounded-md border border-slate-200 bg-white shadow-sm">
|
|
<div className="overflow-x-auto">
|
|
<table className="min-w-full divide-y divide-slate-200 text-sm">
|
|
<thead className="bg-slate-50">
|
|
<tr>
|
|
{columns.map((column) => (
|
|
<th key={column.key} className="whitespace-nowrap px-4 py-3 text-left text-xs font-semibold uppercase text-slate-500">
|
|
{column.label}
|
|
</th>
|
|
))}
|
|
{actions && <th className="whitespace-nowrap px-4 py-3 text-right text-xs font-semibold uppercase text-slate-500">Действия</th>}
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-slate-200">
|
|
{items.length === 0 ? (
|
|
<tr>
|
|
<td colSpan={columns.length + (actions ? 1 : 0)}>
|
|
<EmptyState title={emptyTitle} description={emptyDescription} />
|
|
</td>
|
|
</tr>
|
|
) : (
|
|
items.map((item) => (
|
|
<tr key={getRowKey(item)} className="align-top transition hover:bg-slate-50">
|
|
{columns.map((column) => (
|
|
<td key={column.key} className="px-4 py-3 text-slate-700">
|
|
{column.render(item)}
|
|
</td>
|
|
))}
|
|
{actions && <td className="px-4 py-3 text-right">{actions(item)}</td>}
|
|
</tr>
|
|
))
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|