"use client"; import { useMemo, useState } from "react"; import { PageHeader, DataTable, EmptyState, Input, Pagination, Skeleton } from "@/components/ds"; import { Search } from "lucide-react"; export type CrmTableColumn = { key: string; header: string; className?: string; render?: (row: Record) => React.ReactNode; searchable?: boolean; sortable?: boolean; }; export function CrmTablePage({ title, description, breadcrumbs, columns, rows, empty, actions, toolbar, searchPlaceholder = "جستجو…", filterFn, page, pageSize, onPageChange, selectedIds, onSelectionChange, bulkActions, loading, }: { title: string; description?: string; breadcrumbs?: React.ReactNode; columns: CrmTableColumn[]; rows: Record[]; empty?: React.ReactNode; actions?: React.ReactNode; toolbar?: React.ReactNode; searchPlaceholder?: string; filterFn?: (row: Record, query: string) => boolean; page?: number; pageSize?: number; onPageChange?: (page: number) => void; selectedIds?: Set; onSelectionChange?: (ids: Set) => void; bulkActions?: React.ReactNode; loading?: boolean; }) { const [query, setQuery] = useState(""); const [sortKey, setSortKey] = useState(null); const [sortDir, setSortDir] = useState<"asc" | "desc">("asc"); const filtered = useMemo(() => { let data = rows; const q = query.trim().toLowerCase(); if (q) { data = filterFn ? data.filter((r) => filterFn(r, q)) : data.filter((r) => columns.some((c) => { if (c.searchable === false) return false; const val = r[c.key]; return val != null && String(val).toLowerCase().includes(q); }) ); } if (sortKey) { data = [...data].sort((a, b) => { const av = a[sortKey]; const bv = b[sortKey]; const cmp = String(av ?? "").localeCompare(String(bv ?? ""), "fa"); return sortDir === "asc" ? cmp : -cmp; }); } return data; }, [rows, query, columns, filterFn, sortKey, sortDir]); const paged = useMemo(() => { if (!pageSize || !page) return filtered; const start = (page - 1) * pageSize; return filtered.slice(start, start + pageSize); }, [filtered, page, pageSize]); const displayRows = pageSize && page ? paged : filtered; const toggleSort = (key: string) => { if (sortKey === key) setSortDir((d) => (d === "asc" ? "desc" : "asc")); else { setSortKey(key); setSortDir("asc"); } }; const colsWithSort = columns; if (loading) { return (
); } return (
{breadcrumbs}
setQuery(e.target.value)} placeholder={searchPlaceholder} className="pr-10" aria-label="جستجو" />
{toolbar} {columns.some((c) => c.sortable !== false) ? ( ) : null}
{selectedIds && selectedIds.size > 0 && bulkActions ? (
{selectedIds.size} مورد انتخاب شده {bulkActions}
) : null} ( { const id = String(row.id); const next = new Set(selectedIds); if (e.target.checked) next.add(id); else next.delete(id); onSelectionChange(next); }} aria-label="انتخاب" /> ), }, ...colsWithSort, ] : colsWithSort } rows={displayRows} empty={empty ?? } /> {pageSize && page && onPageChange ? (
) : null}
); } export function CrmStatGrid({ stats, }: { stats: { label: string; value: string | number; hint?: string }[]; }) { return (
{stats.map((s) => (

{s.label}

{s.value}

{s.hint ?

{s.hint}

: null}
))}
); }