Files

358 lines
11 KiB
TypeScript

"use client";
import type { ColumnDef, Row, SortingState } from "@tanstack/react-table";
import {
flexRender,
getCoreRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import * as React from "react";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { cn } from "@/lib/utils";
const formatPrice = (value: number) =>
new Intl.NumberFormat("fa-IR", {
style: "currency",
currency: "IRR",
}).format(value);
type ColumnMeta = {
cellClassName?: string;
};
type Transaction = {
id: number;
invoiceNumber: string;
href: string;
amount: number;
tax?: number;
client: string;
description: string;
};
type DayGroup = {
date: string;
dateTime: string;
transactions: Transaction[];
};
const days: DayGroup[] = [
{
date: "امروز",
dateTime: "2024-05-20",
transactions: [
{
id: 101,
invoiceNumber: "ORD-48392",
href: "/orders/ORD-48392",
amount: 1248.5,
tax: 98.5,
client: "خانه آرورا",
description: "۳ ست ملحفه کتان + بسته‌بندی هدیه",
},
{
id: 102,
invoiceNumber: "PAYOUT-2298",
href: "/payouts/PAYOUT-2298",
amount: 4830,
client: "بازارچه مسیرهای وحشی",
description: "پرداخت هفتگی فروشنده",
},
{
id: 103,
invoiceNumber: "ORD-48310",
href: "/orders/ORD-48310",
amount: 312,
tax: 23.8,
client: "نیلا گرین",
description: "بازپرداخت در حال بررسی",
},
],
},
{
date: "دیروز",
dateTime: "2024-05-19",
transactions: [
{
id: 104,
invoiceNumber: "ORD-48277",
href: "/orders/ORD-48277",
amount: 2940,
tax: 201.3,
client: "شرکت ترک شهری",
description: "۱۲ عدد عصای کوهنوردی کربنی",
},
{
id: 105,
invoiceNumber: "ORD-48261",
href: "/orders/ORD-48261",
amount: 864,
client: "مایا پاتل",
description: "پیش‌سفارش کفش اسپرت نسخه محدود",
},
{
id: 106,
invoiceNumber: "PAYOUT-2297",
href: "/payouts/PAYOUT-2297",
amount: 1980,
client: "لوازم خانگی لومن",
description: "درخواست پرداخت فوری",
},
],
},
{
date: "یکشنبه",
dateTime: "2024-05-18",
transactions: [
{
id: 107,
invoiceNumber: "ORD-48190",
href: "/orders/ORD-48190",
amount: 490,
tax: 34.3,
client: "تأمین اطلس",
description: "سفارش نمونه فروش سازمانی",
},
{
id: 108,
invoiceNumber: "ORD-48175",
href: "/orders/ORD-48175",
amount: 156,
client: "جیمی لی",
description: "پرداخت تقسیمی انجام نشده",
},
],
},
];
type FlatTransaction = Transaction & { dayDate: string; dayDateTime: string };
function flattenDays(src: DayGroup[]): FlatTransaction[] {
const out: FlatTransaction[] = [];
for (const d of src) {
for (const t of d.transactions) {
out.push({ ...t, dayDate: d.date, dayDateTime: d.dateTime });
}
}
return out;
}
export function DataTable24({ className }: { className?: string }) {
const [sorting, setSorting] = React.useState<SortingState>([]);
const data = React.useMemo(() => flattenDays(days), []);
const columns = React.useMemo<ColumnDef<FlatTransaction>[]>(
() => [
{
id: "invoice",
header: "فاکتور",
cell: ({ row }) => {
const t = row.original;
return (
<div className="flex flex-col gap-1">
<a href={t.href} className="text-sm font-semibold">
{t.invoiceNumber}
</a>
</div>
);
},
meta: {
cellClassName: "py-5 pe-6",
},
},
{
id: "amount-status",
header: "مبلغ معامله",
cell: ({ row }) => {
const t = row.original;
return (
<>
<div className="flex gap-6">
<div className="flex-auto">
<div className="flex items-start gap-3">
<div className="text-sm/6 font-medium text-foreground">
{formatPrice(t.amount)}
</div>
{t.tax ? (
<div className="mt-1 text-xs/5 text-muted-foreground">
+ {formatPrice(t.tax)} مالیات
</div>
) : null}
</div>
</div>
</div>
<div className="absolute end-full bottom-0 h-px w-screen bg-border" />
<div className="absolute bottom-0 start-0 h-px w-screen bg-border" />
</>
);
},
meta: {
cellClassName: "relative py-5 pe-6",
},
},
{
id: "client-desc",
header: "مشتری",
cell: ({ row }) => {
const t = row.original;
return (
<>
<div className="text-sm/6 text-foreground">{t.client}</div>
</>
);
},
meta: {
cellClassName: "hidden py-5 pe-6 sm:table-cell",
},
},
{
id: "description",
header: "توضیحات",
cell: ({ row }) => {
const t = row.original;
return <div className="text-sm text-foreground">{t.description}</div>;
},
meta: {
cellClassName: "py-5 pe-6",
},
},
],
[],
);
const table = useReactTable({
data,
columns,
state: { sorting },
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getRowId: (r) => String(r.id),
});
const rowModelRows = table.getRowModel().rows;
const grouped = React.useMemo(() => {
const byDay = new Map<
string,
{ date: string; dateTime: string; rows: Row<FlatTransaction>[] }
>();
for (const r of rowModelRows) {
const key = r.original.dayDateTime;
if (!byDay.has(key)) {
byDay.set(key, {
date: r.original.dayDate,
dateTime: r.original.dayDateTime,
rows: [],
});
}
byDay.get(key)!.rows.push(r);
}
return days.map((d) => byDay.get(d.dateTime)).filter(Boolean) as Array<{
date: string;
dateTime: string;
rows: Row<FlatTransaction>[];
}>;
}, [rowModelRows]);
const columnCount = columns.length;
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
<div className="container">
<div className="w-full">
<div className="mb-8 text-start">
<h2 className="text-xl font-bold tracking-tight">
جدول با سرستون‌های مخفی
</h2>
<p className="mt-2 text-muted-foreground">
جدول تراکنش‌ها با سرستون‌های مخفی و رکورد‌های گروه‌بندی‌شده بر
اساس تاریخ به‌روزرسانی.
</p>
</div>
<div className="overflow-hidden border-t border-border">
<div className="mx-auto max-w-2xl lg:mx-0 lg:max-w-none">
<div className="[&>div]:overflow-visible">
<Table className="w-full text-start text-sm">
<TableHeader className="sr-only [&_*]:border-0 [&_tr]:border-0">
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="border-0">
{headerGroup.headers.map((header) => (
<TableHead
key={header.id}
scope="col"
className="p-0 text-start"
>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{grouped.map((group) => (
<React.Fragment key={group.dateTime}>
<TableRow className="text-sm/6 text-foreground">
<TableHead
scope="colgroup"
colSpan={columnCount}
className="relative isolate border-0 py-2 text-start font-semibold text-foreground"
>
<time dateTime={group.dateTime}>{group.date}</time>
<div className="absolute inset-y-0 end-full -z-10 w-screen border-b border-border bg-muted" />
<div className="absolute inset-y-0 start-0 -z-10 w-screen border-b border-border bg-muted" />
</TableHead>
</TableRow>
{group.rows.map((row) => (
<TableRow key={row.id} className="border-0">
{row.getVisibleCells().map((cell) => {
const meta =
(cell.column.columnDef.meta as ColumnMeta) ??
{};
return (
<TableCell
key={cell.id}
className={cn(
"border-0 p-0 text-start align-top",
meta.cellClassName,
)}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
);
})}
</TableRow>
))}
</React.Fragment>
))}
</TableBody>
</Table>
</div>
</div>
</div>
</div>
</div>
</section>
);
}