diff --git a/src/app/[name]/(Main)/transactions/components/TransactionsTable.tsx b/src/app/[name]/(Main)/transactions/components/TransactionsTable.tsx new file mode 100644 index 0000000..16557da --- /dev/null +++ b/src/app/[name]/(Main)/transactions/components/TransactionsTable.tsx @@ -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 = { + 'SUCCESS': 'موفق', + 'FAILED': 'ناموفق', + 'PENDING': 'در انتظار', + 'CANCELLED': 'لغو شده', + }; + return statusMap[status] || status; +}; + +const getStatusColor = (status: string): string => { + const colorMap: Record = { + '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[] = [ + { + accessorKey: 'order.orderNumber', + header: () =>
شماره سفارش
, + cell: ({ row }) => { + const orderNumber = row.original.order?.orderNumber; + return ( +
{ef(orderNumber?.toString() || '-')}
+ ) + } + }, + { + accessorKey: 'type', + header: () =>
نوع تراکنش
, + cell: ({ row }) => { + const type = row.original.order?.paymentMethod?.description || '-'; + return
{type}
+ } + }, + { + accessorKey: 'amount', + header: () =>
مبلغ
, + cell: ({ row }) => { + const amount = row.original.amount; + const formatted = Math.abs(amount).toLocaleString('fa-IR'); + return
{ef(formatted)} ریال
+ } + }, + { + accessorKey: 'createdAt', + header: () =>
تاریخ
, + cell: ({ row }) => { + const date = formatDate(row.original.createdAt); + return
{ef(date)}
+ } + }, + { + accessorKey: 'status', + header: () =>
وضعیت
, + cell: ({ row }) => { + const status = row.original.status; + const statusText = getStatusText(status); + const statusColor = getStatusColor(status); + + return ( +
+ + {statusText} +
+ ) + } + } +] + +export function TransactionsTable({ transactions }: TransactionsTableProps) { + const [sorting, setSorting] = React.useState([]) + const [columnFilters, setColumnFilters] = React.useState( + [] + ) + const [columnVisibility, setColumnVisibility] = + React.useState({}) + 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 ( +
+
+ + + {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() + )} + + ))} + + )) + ) : ( + + + تراکنشی یافت نشد. + + + )} + +
+
+
+
+ نمایش:{' '} + + {ef(table.getRowModel().rows.length)} + {' '} + از {ef(table.getFilteredRowModel().rows.length)} +
+
+ + + + + + +
+
+
+ ) +} diff --git a/src/app/[name]/(Main)/transactions/hooks/useTransactionData.ts b/src/app/[name]/(Main)/transactions/hooks/useTransactionData.ts index c8dbe49..b2bf696 100644 --- a/src/app/[name]/(Main)/transactions/hooks/useTransactionData.ts +++ b/src/app/[name]/(Main)/transactions/hooks/useTransactionData.ts @@ -20,3 +20,10 @@ export const useConvertScoreToWallet = () => { mutationFn: api.convertScoreToWallet, }); }; + +export const useGetTransactions = () => { + return useQuery({ + queryKey: ["transactions"], + queryFn: api.getTransactions, + }); +}; diff --git a/src/app/[name]/(Main)/transactions/page.tsx b/src/app/[name]/(Main)/transactions/page.tsx index b365f1b..f86578b 100644 --- a/src/app/[name]/(Main)/transactions/page.tsx +++ b/src/app/[name]/(Main)/transactions/page.tsx @@ -1,17 +1,55 @@ -import { DataTableDemo } from '@/components/ui/datatable' +'use client'; import { ef } from '@/lib/helpers/utfNumbers' import { - ArrowLeft, - ArrowRight, MoneyRecive, MoneySend, Wallet } from 'iconsax-react' -import React from 'react' +import React, { useMemo } from 'react' +import { useGetTransactions, useGetUserWallet } from './hooks/useTransactionData'; +import { Transaction } from './types/Types'; +import { TransactionsTable } from './components/TransactionsTable'; +import LoadingOverlay from '@/components/overlays/LoadingOverlay'; function TransactionsReviewIndex() { + const { data: transactionsResponse, isLoading: isLoadingTransactions } = useGetTransactions(); + const { data: walletResponse, isLoading: isLoadingWallet } = useGetUserWallet(); + + const transactions = transactionsResponse?.data || []; + const walletAmount = walletResponse?.data?.wallet || 0; + + const stats = useMemo(() => { + if (!transactions.length) { + return { + totalDeposits: 0, + totalWithdrawals: 0, + }; + } + + const deposits = transactions + .filter((t: Transaction) => t.status === 'SUCCESS' && t.amount > 0) + .reduce((sum: number, t: Transaction) => sum + t.amount, 0); + + const withdrawals = transactions + .filter((t: Transaction) => t.status === 'SUCCESS' && t.amount < 0) + .reduce((sum: number, t: Transaction) => sum + Math.abs(t.amount), 0); + + return { + totalDeposits: deposits, + totalWithdrawals: withdrawals, + }; + }, [transactions]); + + const formatPrice = (amount: number) => { + return ef(amount.toLocaleString('fa-IR')); + }; + + const isLoading = isLoadingTransactions || isLoadingWallet; + return ( -
+
+ {isLoading && } +

لیست تراکنش ها

@@ -24,17 +62,10 @@ function TransactionsReviewIndex() {
مجموع واریز ها - {ef('140,000,000 ریال')} - - - - {ef('30%')} + {formatPrice(stats.totalDeposits)} ریال -
+
مجموع برداشت ها - {ef('140,000,000 ریال')} - - - - {ef('30%')} + {formatPrice(stats.totalWithdrawals)} ریال
-
+
موجودی کیف پول - {ef('140,000,000 ریال')} - - - - {ef('30%')} + {formatPrice(walletAmount)} ریال
- +
) diff --git a/src/app/[name]/(Main)/transactions/service/TransactionService.ts b/src/app/[name]/(Main)/transactions/service/TransactionService.ts index 5f1a8b1..f9b10de 100644 --- a/src/app/[name]/(Main)/transactions/service/TransactionService.ts +++ b/src/app/[name]/(Main)/transactions/service/TransactionService.ts @@ -1,5 +1,9 @@ import { api } from "@/config/axios"; -import { CouponsResponse, WalletResponse } from "../types/Types"; +import { + CouponsResponse, + WalletResponse, + TransactionsResponse, +} from "../types/Types"; export const getMyCoupons = async (): Promise => { const { data } = await api.get("/public/coupons/me"); @@ -15,3 +19,8 @@ export const convertScoreToWallet = async () => { const { data } = await api.post("/public/user/convert-score-to-wallet"); return data; }; + +export const getTransactions = async (): Promise => { + const { data } = await api.get("/public/payments"); + return data; +}; diff --git a/src/app/[name]/(Main)/transactions/types/Types.ts b/src/app/[name]/(Main)/transactions/types/Types.ts index d5637a8..6db6a40 100644 --- a/src/app/[name]/(Main)/transactions/types/Types.ts +++ b/src/app/[name]/(Main)/transactions/types/Types.ts @@ -38,3 +38,63 @@ export interface Wallet { } export type WalletResponse = BaseResponse; + +export interface PaymentMethod { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + restaurant: string; + title: string; + method: string; + gateway: string | null; + description: string; + enabled: boolean; + order: number; + merchantId: string | null; +} + +export interface TransactionOrder { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + user: string; + restaurant: string; + deliveryMethod: string; + address: string | null; + paymentMethod: PaymentMethod; + orderNumber: number; + couponDiscount: number; + couponDetail: unknown | null; + itemsDiscount: number; + totalDiscount: number; + subTotal: number; + tax: number; + deliveryFee: number; + total: number; + totalItems: number; + description: string | null; + tableNumber: string; + status: string; + paymentStatus: string; +} + +export interface Transaction { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + order: TransactionOrder; + amount: number; + authority: string | null; + gateway: string | null; + refId: string | null; + status: string; + cardPan: string | null; + verifyResponse: unknown | null; + paidAt: string | null; + failedAt: string | null; +} + +export type TransactionsResponse = BaseResponse; diff --git a/src/features/auth/components/StepOtp.tsx b/src/features/auth/components/StepOtp.tsx index db1bde8..ecad3dc 100644 --- a/src/features/auth/components/StepOtp.tsx +++ b/src/features/auth/components/StepOtp.tsx @@ -69,6 +69,8 @@ const StepOtp = ({ phone, slug, code }: Props) => { window.location.href = getRedirectPath() }, onError: (error) => { + clear() + window.location.href = getRedirectPath() toast(extractErrorMessage(error), 'error') } })