"use client"; import type { Column, ColumnDef, Row, RowSelectionState, SortingState, } from "@tanstack/react-table"; import { flexRender, getCoreRowModel, getSortedRowModel, useReactTable, } from "@tanstack/react-table"; import { ArrowDown, ArrowUp, Calendar, CheckCheckIcon, CheckCircle, ChevronLeft, ChevronsUpDown, Clock, LoaderIcon, MapPin, MoreHorizontal, Package, PieChart, RefreshCcw, User, } 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 { Checkbox } from "@/components/ui/checkbox"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { cn } from "@/lib/utils"; const formatNumber = (value: number) => value.toLocaleString("fa-IR"); const formatPrice = (value: number) => new Intl.NumberFormat("fa-IR", { style: "currency", currency: "IRR", }).format(value); type UseDataTableOptions = { data: Array; columns: Array>; getRowId?: (row: TData) => string; initialSorting?: SortingState; initialGlobalFilter?: string; initialSelection?: RowSelectionState; enableRowSelection?: boolean; }; export function useDataTable(options: UseDataTableOptions) { const { data, columns, getRowId, initialSorting = [], initialGlobalFilter = "", initialSelection = {}, enableRowSelection = true, } = options; const [sorting, setSorting] = React.useState(initialSorting); const [globalFilter, setGlobalFilter] = React.useState(initialGlobalFilter); const [rowSelection, setRowSelection] = React.useState(initialSelection); const table = useReactTable({ data, columns, getRowId, state: { sorting, globalFilter, rowSelection, }, onSortingChange: setSorting, onRowSelectionChange: setRowSelection, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), enableRowSelection, }); return { table, sorting, setSorting, globalFilter, setGlobalFilter, rowSelection, setRowSelection, }; } type DataTableColumnHeaderProps = { column: Column; title: string; }; export const DataTableColumnHeader = ({ column, title, }: DataTableColumnHeaderProps) => { const canSort = column.getCanSort(); const sorted = column.getIsSorted(); if (!canSort) { return ( {title} ); } return ( ); }; interface DataTableRowActionsProps { row: Row; schema: z.ZodSchema; itemName: string; idField?: string; } export function DataTableRowActions({ row, schema, itemName, idField = "id", }: DataTableRowActionsProps) { const item = schema.parse(row.original) as TData; const itemRecord = item as Record; return ( عملیات navigator.clipboard.writeText( String(itemRecord[idField] || itemRecord.id), ) } > کپی شناسه {itemName} ویرایش {itemName} مشاهده جزئیات ); } // Customer details schema const CustomerSchema = z.object({ name: z.string(), email: z.string(), segment: z.string(), }); // Shipping details schema const ShippingSchema = z.object({ service: z.string(), fulfillment_channel: z.string(), warehouse: z.string(), delivery_eta: z.string(), tracking_number: z.string(), assigned_to: z.string(), }); // Order details schema const OrderDetailsSchema = z.object({ payment_method: z.string(), items: z.number(), coupon_code: z.string().nullable(), gift: z.boolean(), return_status: z.string(), notes: z.string(), }); export const Orderschema = z.object({ id: z.string(), purchased: z.string(), order_status: z.string(), payment_status: z.string(), order_total: z.number(), currency: z.string(), priority: z.string(), order_date: z.string(), last_updated: z.string(), risk_level: z.string(), customer: CustomerSchema, shipping: ShippingSchema, orderDetails: OrderDetailsSchema, }); type Order = z.infer; export const data = [ { id: "#301456", purchased: "کاپشن چرمی", order_status: "آماده تحویل", customer: "سارا احمدی", payment_method: "****7321", payment_status: "پرداخت‌شده", items: 3, order_date: "2025-10-24T08:28:33.060Z", delivery_eta: "2025-10-31T08:28:33.060Z", fulfillment_channel: "FBA", shipping_service: "هوایی سریع", warehouse: "ORD-4", order_total: 243.4, currency: "IRR", customer_email: "sarah.johnson@example.com", customer_segment: "عمده‌فروشی", priority: "کم", coupon_code: "WELCOME10", gift: false, return_status: "تأیید‌شده", assigned_to: "مژگان بیت", risk_level: "کم", tracking_number: "TRK5817960062", last_updated: "2025-10-24T10:28:33.060Z", notes: "سارا احمدی ارسال هوایی سریع را ترجیح می‌دهد.", }, { id: "#789123", purchased: "کفش اسپرت آبی", order_status: "تحویل‌شده", customer: "رضا محمدی", payment_method: "****4509", payment_status: "پرداخت‌شده", items: 2, order_date: "2025-10-22T20:28:33.060Z", delivery_eta: "2025-10-25T20:28:33.060Z", fulfillment_channel: "شخص ثالث", shipping_service: "تحویل یک‌روزه", warehouse: "DFW-2", order_total: 119.96, currency: "IRR", customer_email: "michael.brown@example.com", customer_segment: "بازارچه", priority: "عادی", coupon_code: "VIPONLY", gift: false, return_status: "تأیید‌شده", assigned_to: "هستی سینگ", risk_level: "کم", tracking_number: "TRK3189925655", last_updated: "2025-10-22T22:28:33.060Z", notes: "رضا محمدی ارسال تحویل یک‌روزه را ترجیح می‌دهد.", }, { id: "#654321", purchased: "هودی قرمز", order_status: "تحویل‌شده", customer: "نیلوفر حسینی", payment_method: "****9812", payment_status: "در انتظار پیگیری", items: 4, order_date: "2025-10-21T08:28:33.060Z", delivery_eta: "2025-10-28T08:28:33.060Z", fulfillment_channel: "FBA", shipping_service: "هوایی سریع", warehouse: "ATL-1", order_total: 354.63, currency: "IRR", customer_email: "emily.davis@example.com", customer_segment: "ویژه", priority: "فوری", coupon_code: "VIPONLY", gift: false, return_status: "درخواست‌شده", assigned_to: "هستی سینگ", risk_level: "متوسط", tracking_number: "TRK2861732425", last_updated: "2025-10-21T10:28:33.060Z", notes: "نیلوفر حسینی ارسال هوایی سریع را ترجیح می‌دهد.", }, { id: "#987654", purchased: "عینک آفتابی مشکی", order_status: "تحویل‌نشده", customer: "داوود کریمی", payment_method: "****6734", payment_status: "در انتظار پیگیری", items: 1, order_date: "2025-10-19T20:28:33.060Z", delivery_eta: "2025-10-22T20:28:33.060Z", fulfillment_channel: "شخص ثالث", shipping_service: "تحویل یک‌روزه", warehouse: "LAX-5", order_total: 119.73, currency: "IRR", customer_email: "david.lee@example.com", customer_segment: "بازارچه", priority: "کم", coupon_code: "WELCOME10", gift: false, return_status: "دریافت‌شده", assigned_to: "رایلی نگوین", risk_level: "کم", tracking_number: "TRK5254503688", last_updated: "2025-10-19T22:28:33.060Z", notes: "داوود کریمی ارسال تحویل یک‌روزه را ترجیح می‌دهد.", }, { id: "#432187", purchased: "کوله‌پشتی سبز", order_status: "تحویل‌شده", customer: "لیلا رحیمی", payment_method: "****2901", payment_status: "پرداخت‌شده", items: 2, order_date: "2025-10-18T08:28:33.060Z", delivery_eta: "2025-10-23T08:28:33.060Z", fulfillment_channel: "دراپ‌شیپ", shipping_service: "تحویل دو‌روزه", warehouse: "ATL-1", order_total: 359.28, currency: "IRR", customer_email: "lisa.wang@example.com", customer_segment: "سازمانی", priority: "بالا", coupon_code: "WELCOME10", gift: false, return_status: "تأیید‌شده", assigned_to: "هستی سینگ", risk_level: "کم", tracking_number: "TRK2835974731", last_updated: "2025-10-18T10:28:33.060Z", notes: "لیلا رحیمی ارسال تحویل دو‌روزه را ترجیح می‌دهد.", }, { id: "#567890", purchased: "ساعت نقره‌ای", order_status: "آماده تحویل", customer: "امیر نوری", payment_method: "****8456", payment_status: "در انتظار پیگیری", items: 1, order_date: "2025-10-16T20:28:33.060Z", delivery_eta: "2025-10-22T20:28:33.060Z", fulfillment_channel: "دراپ‌شیپ", shipping_service: "ارسال عادی", warehouse: "ATL-1", order_total: 381.57, currency: "IRR", customer_email: "james.carter@example.com", customer_segment: "سازمانی", priority: "کم", coupon_code: "VIPONLY", gift: false, return_status: "تأیید‌شده", assigned_to: "مژگان بیت", risk_level: "بالا", tracking_number: "TRK1516495925", last_updated: "2025-10-16T22:28:33.060Z", notes: "امیر نوری ارسال عادی را ترجیح می‌دهد.", }, { id: "#234567", purchased: "شال خاکستری", order_status: "تحویل‌شده", customer: "راحله موسوی", payment_method: "****1234", payment_status: "مسترد شده", items: 3, order_date: "2025-10-15T08:28:33.060Z", delivery_eta: "2025-10-18T08:28:33.060Z", fulfillment_channel: "شخص ثالث", shipping_service: "تحویل دو‌روزه", warehouse: "JFK-3", order_total: 398.12, currency: "IRR", customer_email: "rachel.kim@example.com", customer_segment: "سازمانی", priority: "فوری", coupon_code: "SPRING24", gift: false, return_status: "تأیید‌شده", assigned_to: "رایلی نگوین", risk_level: "متوسط", tracking_number: "TRK2114884757", last_updated: "2025-10-15T10:28:33.060Z", notes: "راحله موسوی ارسال تحویل دو‌روزه را ترجیح می‌دهد.", }, { id: "#876543", purchased: "پیراهن بنفش", order_status: "تحویل‌نشده", customer: "توماس علوی", payment_method: "****5678", payment_status: "مسترد جزئی", items: 2, order_date: "2025-10-13T20:28:33.060Z", delivery_eta: "2025-10-19T20:28:33.060Z", fulfillment_channel: "دراپ‌شیپ", shipping_service: "تحویل یک‌روزه", warehouse: "ORD-4", order_total: 337.01, currency: "IRR", customer_email: "thomas.allen@example.com", customer_segment: "عمده‌فروشی", priority: "بالا", coupon_code: null, gift: false, return_status: "درخواست‌شده", assigned_to: "مژگان بیت", risk_level: "متوسط", tracking_number: "TRK0992177244", last_updated: "2025-10-13T22:28:33.060Z", notes: "توماس علوی ارسال تحویل یک‌روزه را ترجیح می‌دهد.", }, { id: "#345678", purchased: "کلاه سفید", order_status: "آماده تحویل", customer: "آنا رحیمی", payment_method: "****9012", payment_status: "پرداخت‌شده", items: 1, order_date: "2025-10-12T08:28:33.060Z", delivery_eta: "2025-10-18T08:28:33.060Z", fulfillment_channel: "دراپ‌شیپ", shipping_service: "ارسال عادی", warehouse: "LAX-5", order_total: 277.54, currency: "IRR", customer_email: "anna.patel@example.com", customer_segment: "سازمانی", priority: "فوری", coupon_code: "WELCOME10", gift: false, return_status: "تأیید‌شده", assigned_to: "هستی سینگ", risk_level: "بالا", tracking_number: "TRK8458630889", last_updated: "2025-10-12T10:28:33.060Z", notes: "آنا رحیمی ارسال عادی را ترجیح می‌دهد.", }, { id: "#456789", purchased: "بوت قهوه‌ای", order_status: "تحویل‌شده", customer: "حامد سنگری", payment_method: "****3456", payment_status: "پرداخت‌شده", items: 4, order_date: "2025-10-10T20:28:33.060Z", delivery_eta: "2025-10-13T20:28:33.060Z", fulfillment_channel: "ارسال فروشنده", shipping_service: "ارسال عادی", warehouse: "ATL-1", order_total: 377.76, currency: "IRR", customer_email: "robert.singh@example.com", customer_segment: "بازارچه", priority: "کم", coupon_code: "WELCOME10", gift: false, return_status: "ندارد", assigned_to: "رایلی نگوین", risk_level: "متوسط", tracking_number: "TRK1335228617", last_updated: "2025-10-10T22:28:33.060Z", notes: "حامد سنگری ارسال عادی را ترجیح می‌دهد.", }, ]; // Define the raw data interface interface RawOrderData { id: string; purchased: string; order_status: string; customer: string; payment_method: string; payment_status: string; items: number; order_date: string; delivery_eta: string; fulfillment_channel: string; shipping_service: string; warehouse: string; order_total: number; currency: string; customer_email: string; customer_segment: string; priority: string; coupon_code: string | null; gift: boolean; return_status: string; assigned_to: string; risk_level: string; tracking_number: string; last_updated: string; notes: string; } // Transform flat data into nested structure export const transformedData = data.map((item: RawOrderData) => ({ id: item.id, purchased: item.purchased, order_status: item.order_status, payment_status: item.payment_status, order_total: item.order_total, currency: item.currency, priority: item.priority, order_date: item.order_date, last_updated: item.last_updated, risk_level: item.risk_level, customer: { name: item.customer, email: item.customer_email, segment: item.customer_segment, }, shipping: { service: item.shipping_service, fulfillment_channel: item.fulfillment_channel, warehouse: item.warehouse, delivery_eta: item.delivery_eta, tracking_number: item.tracking_number, assigned_to: item.assigned_to, }, orderDetails: { payment_method: item.payment_method, items: item.items, coupon_code: item.coupon_code, gift: item.gift, return_status: item.return_status, notes: item.notes, }, })); export const validatedData = Orderschema.array().parse(transformedData); const dateFormatter = new Intl.DateTimeFormat("fa-IR", { dateStyle: "medium", }); const formatDate = (value: string) => { const date = new Date(value); return Number.isNaN(date.getTime()) ? value : dateFormatter.format(date); }; export type StatusOption = { label: string; value: string; icon?: React.ComponentType<{ className?: string }>; }; export const orderStatuses: StatusOption[] = [ { label: "تحویل‌شده", value: "تحویل‌شده", icon: CheckCircle }, { label: "آماده تحویل", value: "آماده تحویل", icon: Package }, { label: "تحویل‌نشده", value: "تحویل‌نشده", icon: Clock }, ]; export const paymentStatuses: StatusOption[] = [ { label: "پرداخت‌شده", value: "پرداخت‌شده", icon: CheckCheckIcon }, { label: "در انتظار پیگیری", value: "در انتظار پیگیری", icon: LoaderIcon }, { label: "مسترد شده", value: "مسترد شده", icon: RefreshCcw }, { label: "مسترد جزئی", value: "مسترد جزئی", icon: PieChart, }, ]; const createStatusLookup = (options: StatusOption[]) => options.reduce>((acc, option) => { acc[option.value] = option; return acc; }, {}); const orderStatusLookup = createStatusLookup(orderStatuses); export const getOrderStatusMeta = (status: string) => orderStatusLookup[status]; export const getStatusVariant = (status: string) => { switch (status) { case "تحویل‌شده": return "default"; case "آماده تحویل": return "secondary"; case "تحویل‌نشده": return "outline"; default: return "outline"; } }; export const columns: ColumnDef[] = [ { id: "select", header: ({ table }) => ( table.toggleAllPageRowsSelected(!!value)} aria-label="انتخاب همه" className="translate-y-[2px]" /> ), cell: ({ row }) => ( row.toggleSelected(!!value)} aria-label="انتخاب رکورد" className="translate-y-[2px]" /> ), enableSorting: false, enableHiding: false, meta: { width: 56, minWidth: 48, headerClassName: "px-2", cellClassName: "px-2", align: "center", }, }, { accessorKey: "id", header: ({ column }) => ( ), cell: ({ row }) => (
{row.original.id}
), enableSorting: true, enableHiding: false, }, { accessorKey: "purchased", header: ({ column }) => ( ), cell: ({ row }) => (
{row.original.purchased}
), enableSorting: false, enableHiding: false, }, { accessorKey: "order_status", header: ({ column }) => ( ), cell: ({ row }) => { const status = row.original.order_status; const statusMeta = getOrderStatusMeta(status); const StatusIcon = statusMeta?.icon; return ( {StatusIcon ? : null} {statusMeta?.label ?? status} ); }, enableSorting: false, }, { accessorKey: "order_total", header: ({ column }) => ( ), cell: ({ row }) => ( {formatPrice(row.original.order_total)} ), meta: { align: "right", className: "tabular-nums" }, enableSorting: true, }, { accessorKey: "customer", header: ({ column }) => ( ), cell: ({ row }) => { const expandHandler = (e: React.MouseEvent) => { e.stopPropagation(); row.toggleExpanded(); }; return (
); }, }, { id: "actions", cell: ({ row }) => (
), }, ]; export function DataTable16({ className }: { className?: string }) { const { table } = useDataTable({ data: validatedData, columns, getRowId: (row) => row.id.toString(), }); return (

جدول با رکورد گسترش‌یافته

جدول داده با رکورد گسترش‌یافته که در عرض ستون والد محدود می‌شود.

{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { return ( {header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext(), )} ); })} ))} {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender( cell.column.columnDef.cell, cell.getContext(), )} ))} {row.getIsExpanded() && (
{/* Left Column */}

سرویس حمل

{row.original.shipping.service}

رهگیری

{row.original.shipping.tracking_number}

تحویل

{formatDate( row.original.shipping.delivery_eta, )}

{/* Right Column */}

انبار

{row.original.shipping.warehouse}

کانال ارسال

{ row.original.shipping .fulfillment_channel }

مسئول

{row.original.shipping.assigned_to}

)}
)) ) : ( نتیجه‌ای یافت نشد )}
); }