Import source: src/components/common/data-table

Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
arsam
2026-09-18 23:10:34 +03:30
parent ca89bce1ab
commit 1d6035a642
+152
View File
@@ -0,0 +1,152 @@
"use client";
import {
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
type ColumnDef,
type SortingState,
} from "@tanstack/react-table";
import { ArrowUpDown, ChevronLeft, ChevronRight, Search } from "lucide-react";
import { useMemo, useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { cn } from "@/lib/utils";
export function DataTable<TData>({
data,
columns,
searchPlaceholder = "Search",
compact = false,
}: {
data: TData[];
columns: ColumnDef<TData>[];
searchPlaceholder?: string;
compact?: boolean;
}) {
const [globalFilter, setGlobalFilter] = useState("");
const [sorting, setSorting] = useState<SortingState>([]);
const memoColumns = useMemo(() => columns, [columns]);
const table = useReactTable({
data,
columns: memoColumns,
state: { globalFilter, sorting },
onGlobalFilterChange: setGlobalFilter,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getSortedRowModel: getSortedRowModel(),
getPaginationRowModel: getPaginationRowModel(),
initialState: { pagination: { pageSize: compact ? 8 : 10 } },
});
return (
<div className="space-y-3">
<div className="flex items-center justify-between gap-3">
<div className="relative w-full max-w-sm">
<Search className="pointer-events-none absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
value={globalFilter}
onChange={(event) => setGlobalFilter(event.target.value)}
placeholder={searchPlaceholder}
className="pl-8"
/>
</div>
<div className="text-xs text-muted-foreground">
{table.getFilteredRowModel().rows.length} results
</div>
</div>
<div className="glass-panel overflow-hidden rounded-lg">
<div className="overflow-x-auto">
<table className="w-full border-collapse text-sm">
<thead className="bg-muted/50">
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th
key={header.id}
className="whitespace-nowrap border-b border-border/60 px-3 py-2.5 text-left text-[11px] font-semibold uppercase tracking-wider text-muted-foreground"
>
{header.isPlaceholder ? null : (
<button
className={cn(
"inline-flex items-center gap-1 transition-colors",
header.column.getCanSort() && "hover:text-primary",
)}
onClick={header.column.getToggleSortingHandler()}
type="button"
>
{flexRender(header.column.columnDef.header, header.getContext())}
{header.column.getCanSort() ? (
<ArrowUpDown className="h-3 w-3" />
) : null}
</button>
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.length ? (
table.getRowModel().rows.map((row) => (
<tr key={row.id} className="transition-colors hover:bg-primary/[0.05]">
{row.getVisibleCells().map((cell) => (
<td
key={cell.id}
className={cn(
"border-b border-border/50 px-3",
compact ? "py-1.5" : "py-2.5",
)}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))
) : (
<tr>
<td
colSpan={columns.length}
className="px-3 py-10 text-center text-sm text-muted-foreground"
>
No matching records.
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
<div className="flex items-center justify-between">
<span className="text-xs text-muted-foreground">
Page {table.getState().pagination.pageIndex + 1} of{" "}
{Math.max(1, table.getPageCount())}
</span>
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
<ChevronLeft className="h-4 w-4" />
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
<ChevronRight className="h-4 w-4" />
</Button>
</div>
</div>
</div>
);
}