transactions

This commit is contained in:
hamid zarghami
2025-12-14 14:14:52 +03:30
parent 8e49baab30
commit 1797063f34
6 changed files with 400 additions and 33 deletions
@@ -0,0 +1,272 @@
'use client'
import * as React from 'react'
import {
ColumnDef,
ColumnFiltersState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable
} from '@tanstack/react-table'
import { Button } from '@/components/ui/button'
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow
} from '@/components/ui/table'
import clsx from 'clsx'
import { ArrowLeft2, ArrowRight2 } from 'iconsax-react'
import { ef } from '@/lib/helpers/utfNumbers'
import { Transaction } from '../types/Types'
interface TransactionsTableProps {
transactions: Transaction[]
}
const formatDate = (dateString: string): string => {
const date = new Date(dateString);
return date.toLocaleDateString('fa-IR', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
});
};
const getStatusText = (status: string): string => {
const statusMap: Record<string, string> = {
'SUCCESS': 'موفق',
'FAILED': 'ناموفق',
'PENDING': 'در انتظار',
'CANCELLED': 'لغو شده',
};
return statusMap[status] || status;
};
const getStatusColor = (status: string): string => {
const colorMap: Record<string, string> = {
'SUCCESS': 'bg-[#00BA4B]',
'FAILED': 'bg-red-500',
'PENDING': 'bg-yellow-500',
'CANCELLED': 'bg-gray-500',
};
return colorMap[status] || 'bg-transparent border border-primary';
};
export const columns: ColumnDef<Transaction>[] = [
{
accessorKey: 'order.orderNumber',
header: () => <div className='text-right pr-6'>شماره سفارش</div>,
cell: ({ row }) => {
const orderNumber = row.original.order?.orderNumber;
return (
<div className='text-right pr-6'>{ef(orderNumber?.toString() || '-')}</div>
)
}
},
{
accessorKey: 'type',
header: () => <div className='text-center'>نوع تراکنش</div>,
cell: ({ row }) => {
const type = row.original.order?.paymentMethod?.description || '-';
return <div className='text-center'>{type}</div>
}
},
{
accessorKey: 'amount',
header: () => <div className='text-center'>مبلغ</div>,
cell: ({ row }) => {
const amount = row.original.amount;
const formatted = Math.abs(amount).toLocaleString('fa-IR');
return <div className='text-center'>{ef(formatted)} ریال</div>
}
},
{
accessorKey: 'createdAt',
header: () => <div className='text-center'>تاریخ</div>,
cell: ({ row }) => {
const date = formatDate(row.original.createdAt);
return <div className='text-center'>{ef(date)}</div>
}
},
{
accessorKey: 'status',
header: () => <div className='text-center'>وضعیت</div>,
cell: ({ row }) => {
const status = row.original.status;
const statusText = getStatusText(status);
const statusColor = getStatusColor(status);
return (
<div className='text-right flex items-center justify-center gap-x-2'>
<span
className={clsx(
'size-2 rounded-full',
statusColor
)}
></span>
{statusText}
</div>
)
}
}
]
export function TransactionsTable({ transactions }: TransactionsTableProps) {
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
)
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({})
const [rowSelection, setRowSelection] = React.useState({})
const table = useReactTable({
data: transactions,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection
},
initialState: {
pagination: {
pageSize: 8
}
}
})
return (
<div className='w-full'>
<div className='rounded-3xl min-h-[507px] w-full'>
<Table className='w-[727px] md:w-full'>
<TableHeader>
{table.getHeaderGroups().map(headerGroup => (
<TableRow
className='dark:border-neutral-400 border-[#EAEDF5] h-14'
key={headerGroup.id}
>
{headerGroup.headers.map(header => {
return (
<TableHead
key={header.id}
className='font-light text-xs text-disabled-text text-center'
>
{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}
className='text-center w-full border-b-[#EAEDF5] dark:border-b-background text-xs odd:bg-[#E9EBF433] bg-border dark:odd:bg-background/80 even:bg-container border-0 border-b! h-14 border-r-4! border-r-transparent active:border-r-primary dark:active:border-r-foreground'
>
{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>
<div className='flex items-center justify-end space-x-2 pt-4 px-7 pb-11 select-none'>
<div className='flex-1 text-sm text-disabled-text'>
نمایش:{' '}
<span className='text-primary dark:text-foreground'>
{ef(table.getRowModel().rows.length)}
</span>{' '}
از {ef(table.getFilteredRowModel().rows.length)}
</div>
<div className='space-x-2 justify-center flex'>
<Button
variant='ghost'
size='sm'
className='size-6'
onClick={() => table.previousPage()}
hidden={!table.getCanPreviousPage()}
>
<ArrowRight2 size={12} className='stroke-[#A8ABBF]' />
</Button>
<Button
variant='secondary'
size='sm'
className='dark:bg-disabled bg-[#EAECF5] text-xs size-6'
onClick={() => table.previousPage()}
hidden={!table.getCanPreviousPage()}
>
{ef(table.getState().pagination.pageIndex)}
</Button>
<Button
variant='default'
className='text-white dark:text-foreground text-xs size-6'
size='sm'
hidden={!table.getCanPreviousPage() && !table.getCanNextPage()}
>
{ef(table.getState().pagination.pageIndex + 1)}
</Button>
<Button
variant='secondary'
size='sm'
className='dark:bg-disabled bg-[#EAECF5] text-xs size-6'
onClick={() => table.nextPage()}
hidden={!table.getCanNextPage()}
>
{ef(table.getState().pagination.pageIndex + 2)}
</Button>
<Button
variant='ghost'
size='sm'
className='size-6'
onClick={() => table.nextPage()}
hidden={!table.getCanNextPage()}
>
<ArrowLeft2 size={12} className='stroke-[#A8ABBF]' />
</Button>
</div>
</div>
</div>
)
}