Files
reui-blocks/blocks/data-table6.tsx
T

454 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import {
type Column,
type ColumnDef,
flexRender,
getCoreRowModel,
getPaginationRowModel,
getSortedRowModel,
type SortingState,
type Table as TableType,
useReactTable,
} from "@tanstack/react-table";
import { ArrowDown, ArrowUp, ChevronsUpDown } from "lucide-react";
import * as React from "react";
import { z } from "zod";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
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);
const formatNumber = (value: number) =>
value.toLocaleString("fa-IR");
type UseDataTableOptions<TData> = {
data: Array<TData>;
columns: Array<ColumnDef<TData, unknown>>;
getRowId?: (row: TData) => string;
initialSorting?: SortingState;
};
export function useDataTable<TData>(options: UseDataTableOptions<TData>) {
const { data, columns, getRowId, initialSorting = [] } = options;
const [sorting, setSorting] = React.useState<SortingState>(initialSorting);
const table = useReactTable({
data,
columns,
getRowId,
state: { sorting },
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});
return {
table,
sorting,
setSorting,
};
}
type DataTableColumnHeaderProps<TData, TValue> = {
column: Column<TData, TValue>;
title: string;
};
export const DataTableColumnHeader = <TData, TValue>({
column,
title,
}: DataTableColumnHeaderProps<TData, TValue>) => {
const canSort = column.getCanSort();
const sorted = column.getIsSorted();
if (!canSort) {
return (
<span className="flex h-8 items-center text-sm font-medium text-foreground">
{title}
</span>
);
}
return (
<Button
variant="ghost"
size="sm"
className="flex h-8 items-center gap-2 px-0 text-sm font-medium text-foreground"
onClick={() => column.toggleSorting(sorted === "asc")}
>
<span>{title}</span>
{sorted === "desc" ? (
<ArrowDown className="h-4 w-4" />
) : sorted === "asc" ? (
<ArrowUp className="h-4 w-4" />
) : (
<ChevronsUpDown className="h-4 w-4 opacity-50" />
)}
</Button>
);
};
type DataTablePaginationProps<TData> = {
table: TableType<TData>;
};
export const DataTablePagination = <TData,>({
table,
}: DataTablePaginationProps<TData>) => {
const currentPage = table.getState().pagination.pageIndex;
const pageSize = table.getState().pagination.pageSize;
const totalRows = table.getRowCount();
const startRow = currentPage * pageSize + 1;
const endRow = Math.min((currentPage + 1) * pageSize, totalRows);
return (
<div className="flex items-center justify-end gap-2 py-4">
<div className="flex-1 text-sm text-muted-foreground">
<span>
{formatNumber(startRow)}–{formatNumber(endRow)}
</span>{" "}
از <span>{formatNumber(totalRows)}</span>
</div>
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
قبلی
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
بعدی
</Button>
</div>
</div>
);
};
export const schema = z.object({
id: z.string(),
item: z.string(),
type: z.string(),
stock: z.boolean(),
sku: z.string(),
price: z.number(),
availability: z.array(z.enum(["فروشگاه", "آنلاین"])),
});
const data = [
{
id: "prod-001",
item: "قاب تبلت",
type: "الکترونیک",
stock: true,
sku: "TC-001",
price: 83.24,
availability: ["فروشگاه", "آنلاین"],
},
{
id: "prod-002",
item: "ساعت هوشمند",
type: "الکترونیک",
stock: true,
sku: "SW-002",
price: 246.27,
availability: ["فروشگاه", "آنلاین"],
},
{
id: "prod-003",
item: "پلیور پشمی",
type: "لوازم جانبی",
stock: true,
sku: "WS-003",
price: 168.27,
availability: ["فروشگاه"],
},
{
id: "prod-004",
item: "هدفون بی‌سیم",
type: "الکترونیک",
stock: true,
sku: "WE-004",
price: 107.75,
availability: ["فروشگاه", "آنلاین"],
},
{
id: "prod-005",
item: "کاور لپ‌تاپ",
type: "الکترونیک",
stock: true,
sku: "LS-005",
price: 248.02,
availability: ["فروشگاه", "آنلاین"],
},
{
id: "prod-006",
item: "کفش دویدن",
type: "کفش",
stock: true,
sku: "RS-006",
price: 208.26,
availability: ["فروشگاه"],
},
{
id: "prod-007",
item: "کاپشن زمستانی",
type: "پوشاک",
stock: true,
sku: "WJ-007",
price: 148.06,
availability: ["فروشگاه"],
},
{
id: "prod-008",
item: "قاب گوشی",
type: "لوازم جانبی",
stock: true,
sku: "PC-008",
price: 298.08,
availability: ["فروشگاه", "آنلاین"],
},
{
id: "prod-009",
item: "ردیاب تناسب اندام",
type: "پوشاک",
stock: true,
sku: "FT-009",
price: 222.09,
availability: ["فروشگاه"],
},
{
id: "prod-010",
item: "عینک آفتابی",
type: "لوازم جانبی",
stock: true,
sku: "SG-010",
price: 60.17,
availability: ["فروشگاه"],
},
{
id: "prod-011",
item: "بوت روزمره",
type: "کفش",
stock: true,
sku: "CB-011",
price: 276.15,
availability: ["فروشگاه", "آنلاین"],
},
{
id: "prod-012",
item: "کفش پاشنه‌بلند",
type: "پوشاک",
stock: true,
sku: "HH-012",
price: 85.47,
availability: ["فروشگاه"],
},
{
id: "prod-013",
item: "صندل ساحلی",
type: "کفش",
stock: true,
sku: "BS-013",
price: 41.25,
availability: ["فروشگاه", "آنلاین"],
},
{
id: "prod-014",
item: "گردنبند طلا",
type: "لوازم جانبی",
stock: true,
sku: "GN-014",
price: 201.47,
availability: ["فروشگاه", "آنلاین"],
},
{
id: "prod-015",
item: "اسپیکر بلوتوث",
type: "الکترونیک",
stock: true,
sku: "BS-015",
price: 297.37,
availability: ["فروشگاه", "آنلاین"],
},
];
export const validatedData = schema.array().parse(data);
export const columns: ColumnDef<z.infer<typeof schema>>[] = [
{
accessorKey: "sku",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="کد کالا" />
),
enableSorting: true,
enableHiding: false,
},
{
accessorKey: "item",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="محصول" />
),
cell: ({ row }) => (
<div className="font-medium">{row.getValue("item")}</div>
),
enableSorting: true,
enableHiding: false,
},
{
accessorKey: "type",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="دسته‌بندی" />
),
enableSorting: true,
enableHiding: false,
},
{
accessorKey: "stock",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="موجود در انبار" />
),
cell: ({ row }) => {
const inStock: boolean = row.getValue("stock");
return inStock ? "بله" : "خیر";
},
enableSorting: false,
enableHiding: false,
},
{
accessorKey: "price",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="قیمت" />
),
cell: ({ row }) => {
const price = parseFloat(row.getValue("price"));
return <div className="font-medium">{formatPrice(price)}</div>;
},
enableSorting: true,
enableHiding: false,
},
{
accessorKey: "availability",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="دسترس‌پذیر در" />
),
cell: ({ row }) => {
const availability: ("فروشگاه" | "آنلاین")[] =
row.getValue("availability");
return (
<div className="flex gap-2">
{availability.map((location) => (
<Badge key={location} variant="secondary">
{location}
</Badge>
))}
</div>
);
},
enableSorting: false,
enableHiding: false,
},
];
export const DataTable6 = ({ className }: { className?: string }) => {
const { table } = useDataTable({
data: validatedData,
columns,
getRowId: (row) => row.id.toString(),
});
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 rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id} className="px-0">
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
نتیجه‌ای یافت نشد.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<DataTablePagination table={table} />
</div>
</div>
</section>
);
};