TorbatYar/frontend/modules/hospitality/design-system/HospitalityTablePage.tsx
Mortezakoohjani 065c053c16 Add complete Torbat Food hospitality frontend connected to backend APIs.
Introduces modules/hospitality with 54 routes, BFF proxy, capability-gated nav, CRUD factory, and architecture validation docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 20:54:26 +03:30

79 lines
2.3 KiB
TypeScript

"use client";
import { useMemo, useState } from "react";
import { PageHeader, DataTable, EmptyState, Input } from "@/components/ds";
import { Search } from "lucide-react";
import { HospitalityBreadcrumbs } from "@/modules/hospitality/components/HospitalityBreadcrumbs";
export type HospitalityTableColumn = {
key: string;
header: string;
className?: string;
render?: (row: Record<string, unknown>) => React.ReactNode;
searchable?: boolean;
};
export function HospitalityTablePage({
title,
description,
breadcrumb,
columns,
rows,
empty,
actions,
searchPlaceholder = "جستجو…",
filterFn,
toolbar,
}: {
title: string;
description?: string;
breadcrumb?: string;
columns: HospitalityTableColumn[];
rows: Record<string, unknown>[];
empty?: React.ReactNode;
actions?: React.ReactNode;
searchPlaceholder?: string;
filterFn?: (row: Record<string, unknown>, query: string) => boolean;
toolbar?: React.ReactNode;
}) {
const [query, setQuery] = useState("");
const filtered = useMemo(() => {
const q = query.trim().toLowerCase();
if (!q) return rows;
if (filterFn) return rows.filter((r) => filterFn(r, q));
return rows.filter((r) =>
columns.some((c) => {
if (c.searchable === false) return false;
const val = r[c.key];
return val != null && String(val).toLowerCase().includes(q);
})
);
}, [rows, query, columns, filterFn]);
return (
<div>
{breadcrumb ? <HospitalityBreadcrumbs current={breadcrumb} /> : null}
<PageHeader title={title} description={description} actions={actions} />
<div className="mb-4 flex flex-wrap items-center gap-3">
<div className="relative max-w-md flex-1">
<Search className="pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-[var(--muted)]" />
<Input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder={searchPlaceholder}
className="pr-10"
aria-label="جستجو"
/>
</div>
{toolbar}
</div>
<DataTable
columns={columns}
rows={filtered}
empty={empty ?? <EmptyState title="موردی یافت نشد" />}
/>
</div>
);
}