Files
MikrotikManager/components/data-table.tsx
T
2026-05-02 01:17:08 +07:00

77 lines
2.3 KiB
TypeScript

"use client"
import { useMemo, useState } from "react"
import { Card } from "@/components/ui/card"
import { SearchIcon } from "lucide-react"
export interface Column<T> {
key: string
label: string
render: (row: T) => React.ReactNode
}
interface DataTableProps<T extends { id: string }> {
data: T[]
columns: Column<T>[]
searchPlaceholder?: string
searchKeys?: (keyof T)[]
}
export function DataTable<T extends { id: string }>({
data,
columns,
searchPlaceholder = "Поиск…",
searchKeys = [],
}: DataTableProps<T>) {
const [search, setSearch] = useState("")
const filtered = useMemo(() => {
if (!search || searchKeys.length === 0) return data
const s = search.toLowerCase()
return data.filter((row) =>
searchKeys.some((k) => String(row[k]).toLowerCase().includes(s)),
)
}, [data, search, searchKeys])
return (
<Card>
<div className="flex items-center gap-2 px-5 py-3 border-b">
<div className="flex items-center gap-2 h-8 px-3 border border-input rounded-md bg-background min-w-[220px]">
<SearchIcon className="size-3.5 text-muted-foreground shrink-0" />
<input
className="flex-1 bg-transparent outline-none placeholder:text-muted-foreground text-sm"
placeholder={searchPlaceholder}
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</div>
<span className="text-sm text-muted-foreground ml-1">{filtered.length} записей</span>
</div>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-border text-xs text-muted-foreground">
{columns.map((col) => (
<th key={col.key} className="text-left font-medium px-5 py-3">
{col.label}
</th>
))}
</tr>
</thead>
<tbody className="divide-y divide-border">
{filtered.map((row) => (
<tr key={row.id} className="hover:bg-muted/40 transition-colors">
{columns.map((col) => (
<td key={col.key} className="px-5 py-3">
{col.render(row)}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</Card>
)
}