copy base dmenu to dkala
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
'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 { ArrowLeft2, ArrowRight2 } from 'iconsax-react'
|
||||
import { ef } from '@/lib/helpers/utfNumbers'
|
||||
import { Transaction } from '../types/Types'
|
||||
import Status from '@/components/utils/Status'
|
||||
|
||||
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'
|
||||
});
|
||||
};
|
||||
|
||||
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;
|
||||
return <Status status={status} className='text-right' />
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
'use client';
|
||||
import { ef } from '@/lib/helpers/utfNumbers'
|
||||
import { Cup, Star1, MoneyRecive } from 'iconsax-react'
|
||||
import React from 'react'
|
||||
import { useConvertScoreToWallet, useGetUserPointsBalance, useGetUserWallet } from '../../hooks/useTransactionData';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useGetAbout } from '../../../about/hooks/useAboutData';
|
||||
import { toast } from '@/components/Toast';
|
||||
import { extractErrorMessage } from '@/lib/func';
|
||||
|
||||
function TransactionsIndex() {
|
||||
|
||||
const { data: userWallet } = useGetUserWallet();
|
||||
const { data: userPointsBalance } = useGetUserPointsBalance();
|
||||
const { data: aboutData } = useGetAbout();
|
||||
const { mutate: convertScoreToWallet, isPending: isConverting } = useConvertScoreToWallet();
|
||||
const userPoints = userPointsBalance?.data?.balance ?? 0;
|
||||
const userWalletAmount = userWallet?.data?.balance ?? 0;
|
||||
const conversionRate = Number(aboutData?.data?.score?.purchaseScore ?? 1000);
|
||||
const calculatedAmount = Math.floor(userPoints / conversionRate) * Number(aboutData?.data?.score?.purchaseAmount ?? 1);
|
||||
|
||||
const handleConvert = async () => {
|
||||
convertScoreToWallet(undefined, {
|
||||
onSuccess: () => {
|
||||
toast('تبدیل امتیاز به اعتبار با موفقیت انجام شد', 'success');
|
||||
window.location.reload();
|
||||
},
|
||||
onError: (error) => {
|
||||
toast(extractErrorMessage(error), 'error');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<section className='pt-6 pb-10'>
|
||||
<h1 className='font-medium dark:text-foreground'>باشگاه مشتریان</h1>
|
||||
|
||||
<section
|
||||
aria-label="درباره باشگاه مشتریان"
|
||||
className='mt-3 bg-container dark:bg-container rounded-normal grid grid-cols-4 items-center border border-transparent dark:border-border/50'
|
||||
>
|
||||
<div className='col-span-3 py-5 pe-7 ps-5 relative h-36.5'>
|
||||
<div className='w-full h-full absolute top-0 left-0 overflow-clip text-muted-foreground/30 dark:text-foreground/20'>
|
||||
<svg width="301" height="146" viewBox="0 0 301 146" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.15">
|
||||
<path d="M262.479 152.795C202.8 122.128 120.111 132.267 77.763 175.315C35.4154 218.362 49.3247 278.14 109.004 308.807C168.683 339.474 251.373 329.335 293.721 286.287C336.069 243.24 322.159 183.462 262.479 152.795Z" stroke="currentColor" />
|
||||
<path d="M298.808 115.866C239.129 85.1988 156.44 95.3379 114.092 138.385C71.7445 181.433 85.6538 241.21 145.333 271.877C205.012 302.544 287.702 292.405 330.05 249.358C372.398 206.31 358.488 146.533 298.808 115.866Z" stroke="currentColor" />
|
||||
<path d="M347.543 66.3263C287.863 35.6595 205.174 45.7986 162.826 88.8461C120.479 131.894 134.388 191.671 194.067 222.338C253.747 253.005 336.437 242.866 378.784 199.819C421.132 156.771 407.222 96.9933 347.543 66.3263Z" stroke="currentColor" />
|
||||
<path d="M416.657 -3.92933C356.977 -34.5961 274.288 -24.457 231.941 18.5905C189.593 61.638 203.502 121.415 263.182 152.082C322.861 182.749 405.551 172.61 447.899 129.563C490.246 86.5153 476.336 26.7377 416.657 -3.92933Z" stroke="currentColor" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div className='flex flex-col justify-between h-full relative z-10'>
|
||||
<div>
|
||||
<h2 className='font-bold text-base dark:text-foreground'>در باشگاه مشتریان</h2>
|
||||
<p className='text-xs mt-[7px] dark:text-disabled-text'>با امتیازی که داری سفارش بده!</p>
|
||||
</div>
|
||||
|
||||
<p className='text-xs font-medium dark:text-foreground'>مجموع امتیازات شما: {ef(userPointsBalance?.data?.balance?.toString() ?? '0')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className='max-w-36 place-self-end col-span-1 h-full text-end py-4 px-5.5 flex flex-col justify-between rounded-e-normal bg-linear-to-br from-primary to-menu-header'
|
||||
>
|
||||
<p className='text-sm2 text-white leading-tight'>
|
||||
<span className='font-normal'>Customer</span><br />
|
||||
<span className='font-medium'>Club</span>
|
||||
</p>
|
||||
|
||||
<div className='place-self-center w-fit border max-h-6 flex justify-center items-center gap-1 border-white rounded-sm py-1 px-1.5'>
|
||||
<Star1 className='fill-white stroke-white' size={8} />
|
||||
<Star1 className='fill-white stroke-white' size={8} />
|
||||
<Star1 className='fill-white stroke-white' size={8} />
|
||||
<Cup className='stroke-white' size={16} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* بخش نمایش اعتبار کاربر */}
|
||||
<section className='mt-6 bg-container dark:bg-container rounded-normal p-5 border border-transparent dark:border-border/50'>
|
||||
<div className='flex items-center gap-2 mb-4'>
|
||||
<MoneyRecive color='currentColor' size={24} className='text-primary dark:text-foreground' variant='Bold' />
|
||||
<h2 className='font-bold text-base dark:text-foreground'>اعتبار شما</h2>
|
||||
</div>
|
||||
<div className='bg-linear-to-l from-primary/10 to-primary/5 dark:from-primary/20 dark:to-primary/10 rounded-md p-4'>
|
||||
<div className='flex justify-between items-center'>
|
||||
<span className='text-sm text-muted-foreground dark:text-disabled-text'>موجودی کیف پول:</span>
|
||||
<span className='font-bold text-lg text-primary dark:text-foreground'>{ef(userWalletAmount.toLocaleString())} تومان</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* بخش تبدیل امتیاز به اعتبار */}
|
||||
<section className='mt-6 bg-container dark:bg-container rounded-normal p-5 border border-transparent dark:border-border/50'>
|
||||
<div className='flex items-center gap-2 mb-4'>
|
||||
<MoneyRecive color='currentColor' size={24} className='text-primary dark:text-foreground' variant='Bold' />
|
||||
<h2 className='font-bold text-base dark:text-foreground'>تبدیل امتیاز به اعتبار</h2>
|
||||
</div>
|
||||
|
||||
<p className='text-sm text-muted-foreground dark:text-disabled-text mb-4'>
|
||||
هر {ef(aboutData?.data?.score?.purchaseScore?.toString() ?? '0')} امتیاز معادل {ef(aboutData?.data?.score?.purchaseAmount?.toString() ?? '0')} تومان اعتبار است
|
||||
</p>
|
||||
|
||||
<div className='space-y-4'>
|
||||
<div className='bg-accent/50 dark:bg-disabled/30 rounded-md p-4 space-y-2 border border-border/50 dark:border-border'>
|
||||
<div className='flex justify-between items-center'>
|
||||
<span className='text-sm text-muted-foreground dark:text-disabled-text'>امتیاز شما:</span>
|
||||
<span className='font-bold text-lg dark:text-foreground'>{ef(userPoints.toString())}</span>
|
||||
</div>
|
||||
<div className='flex justify-between items-center'>
|
||||
<span className='text-sm text-muted-foreground dark:text-disabled-text'>دریافت میکنید:</span>
|
||||
<span className='font-bold text-lg text-primary dark:text-foreground'>{ef(calculatedAmount.toString())} تومان</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={handleConvert}
|
||||
disabled={userPoints <= 0 || isConverting}
|
||||
className='w-full text-xs dark:bg-white dark:text-gray-900 dark:hover:bg-white/90'
|
||||
>
|
||||
{isConverting ? 'در حال تبدیل...' : 'تبدیل تمام امتیازات'}
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default TransactionsIndex
|
||||
@@ -0,0 +1,123 @@
|
||||
'use client';
|
||||
import { Button } from '@/components/ui/button'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useGetMyCoupons } from '../hooks/useTransactionData';
|
||||
import { toast } from '@/components/Toast';
|
||||
import type { Coupon } from '../types/Types';
|
||||
|
||||
function TransactionsIndex() {
|
||||
const { data: myCoupons, isLoading } = useGetMyCoupons();
|
||||
|
||||
const handleCopyCode = useCallback(async (code: string) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(code);
|
||||
toast('کد تخفیف با موفقیت کپی شد', 'success');
|
||||
} catch {
|
||||
toast('خطا در کپی کردن کد تخفیف', 'error');
|
||||
}
|
||||
}, []);
|
||||
|
||||
const formatDiscount = useCallback((coupon: Coupon) => {
|
||||
if (coupon.type === 'PERCENTAGE') {
|
||||
return `${coupon.value}%`;
|
||||
}
|
||||
return `${coupon.value.toLocaleString('fa-IR')} تومان`;
|
||||
}, []);
|
||||
|
||||
const coupons = myCoupons?.data ?? [];
|
||||
|
||||
return (
|
||||
<section className='pt-6'>
|
||||
<h1 className='font-medium'>کدهای تخفیف</h1>
|
||||
|
||||
<section
|
||||
aria-label="درباره کدهای تخفیف"
|
||||
className='mt-3 bg-container rounded-normal grid grid-cols-4 items-center'
|
||||
>
|
||||
<div className='col-span-3 py-9.5 pe-7 ps-5 relative'>
|
||||
<div className='w-full h-full absolute top-0 left-0 overflow-clip'>
|
||||
<svg width="301" height="146" viewBox="0 0 301 146" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.15">
|
||||
<path d="M262.479 152.795C202.8 122.128 120.111 132.267 77.763 175.315C35.4154 218.362 49.3247 278.14 109.004 308.807C168.683 339.474 251.373 329.335 293.721 286.287C336.069 243.24 322.159 183.462 262.479 152.795Z" stroke="#AFAFAF" />
|
||||
<path d="M298.808 115.866C239.129 85.1988 156.44 95.3379 114.092 138.385C71.7445 181.433 85.6538 241.21 145.333 271.877C205.012 302.544 287.702 292.405 330.05 249.358C372.398 206.31 358.488 146.533 298.808 115.866Z" stroke="#AFAFAF" />
|
||||
<path d="M347.543 66.3263C287.863 35.6595 205.174 45.7986 162.826 88.8461C120.479 131.894 134.388 191.671 194.067 222.338C253.747 253.005 336.437 242.866 378.784 199.819C421.132 156.771 407.222 96.9933 347.543 66.3263Z" stroke="#AFAFAF" />
|
||||
<path d="M416.657 -3.92933C356.977 -34.5961 274.288 -24.457 231.941 18.5905C189.593 61.638 203.502 121.415 263.182 152.082C322.861 182.749 405.551 172.61 447.899 129.563C490.246 86.5153 476.336 26.7377 416.657 -3.92933Z" stroke="#AFAFAF" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<h2 className='font-bold text-base'>کدهای تخفیف شما</h2>
|
||||
<p className='text-xs mt-[7px]'>کد تخفیف مورد نظر را کپی کنید و در بخش پرداختها استفاده کنید</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className='col-span-1 h-full text-end py-4 px-5.5 flex flex-col justify-between rounded-e-normal bg-linear-to-br from-primary to-menu-header'
|
||||
>
|
||||
<p className='text-sm2 text-white leading-tight'>
|
||||
<span className='font-medium'>Your</span><br />
|
||||
<span className='font-bold'>Coupon</span>
|
||||
</p>
|
||||
|
||||
<svg className='w-full' height="20" viewBox="0 0 72 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.0033 0H0.845703V20H4.0033V0Z" fill="white" />
|
||||
<path d="M9.266 0H6.1084V20H9.266V0Z" fill="white" />
|
||||
<path d="M12.4236 0H11.3711V20H12.4236V0Z" fill="white" />
|
||||
<path d="M16.6344 0H14.5293V20H16.6344V0Z" fill="white" />
|
||||
<path d="M19.7916 0H17.6865V20H19.7916V0Z" fill="white" />
|
||||
<path d="M24.0018 0H22.9492V20H24.0018V0Z" fill="white" />
|
||||
<path d="M28.2113 0H25.0537V20H28.2113V0Z" fill="white" />
|
||||
<path d="M30.3182 0H29.2656V20H30.3182V0Z" fill="white" />
|
||||
<path d="M33.4744 0H32.4219V20H33.4744V0Z" fill="white" />
|
||||
<path d="M36.6324 0H34.5273V20H36.6324V0Z" fill="white" />
|
||||
<path d="M42.9478 0H40.8428V20H42.9478V0Z" fill="white" />
|
||||
<path d="M47.1576 0H44V20H47.1576V0Z" fill="white" />
|
||||
<path d="M49.2625 0H48.21V20H49.2625V0Z" fill="white" />
|
||||
<path d="M55.5785 0H52.4209V20H55.5785V0Z" fill="white" />
|
||||
<path d="M57.6824 0H56.6299V20H57.6824V0Z" fill="white" />
|
||||
<path d="M62.9455 0H58.7354V20H62.9455V0Z" fill="white" />
|
||||
<path d="M66.1031 0H63.998V20H66.1031V0Z" fill="white" />
|
||||
<path d="M71.3666 0H68.209V20H71.3666V0Z" fill="white" />
|
||||
</svg>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{isLoading ? (
|
||||
<div className='flex flex-col items-center justify-center gap-2 py-12 text-sm2 text-muted-foreground mt-6'>
|
||||
<span className='h-4 w-4 animate-spin rounded-full border border-dashed border-foreground border-t-transparent'></span>
|
||||
<p>در حال دریافت کدهای تخفیف...</p>
|
||||
</div>
|
||||
) : coupons.length === 0 ? (
|
||||
<div className='flex flex-col items-center justify-center gap-2 py-12 text-sm2 text-muted-foreground mt-6'>
|
||||
<p>کد تخفیفی موجود نیست</p>
|
||||
</div>
|
||||
) : (
|
||||
<section className="mt-6 space-y-6" aria-label="لیست کدهای تخفیف">
|
||||
{coupons.map((coupon) => (
|
||||
<article
|
||||
key={coupon.id}
|
||||
className='bg-container rounded-normal flex justify-between items-center'
|
||||
>
|
||||
<div className='py-4 pe-7 ps-5 border-e-2 w-full border-border border-dashed'>
|
||||
<h3 className='font-bold text-sm2'>مبلغ تخفیف: {formatDiscount(coupon)}</h3>
|
||||
<p className='text-xs mt-[7px]'>{coupon.description || 'قابل استفاده برای همهی منوها و محصولات'}</p>
|
||||
<Button
|
||||
className='w-fit text-white font-bold px-10 py-1.5 mt-4 text-xs rounded-lg dark:bg-white dark:text-black dark:hover:bg-gray-100'
|
||||
aria-label="کپی کد تخفیف"
|
||||
onClick={() => handleCopyCode(coupon.code)}
|
||||
>
|
||||
کپی کد
|
||||
</Button>
|
||||
</div>
|
||||
<div className='w-14 h-full flex flex-1 justify-center items-center'>
|
||||
<p className='text-sm2 transform -rotate-90 text-[#B2B2B2] font-medium' aria-label="کد تخفیف">
|
||||
{coupon.code}
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default TransactionsIndex
|
||||
@@ -0,0 +1,6 @@
|
||||
export const enum PaymentStatusEnum {
|
||||
Pending = "pending",
|
||||
Paid = "paid",
|
||||
Failed = "failed",
|
||||
Refunded = "refunded",
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import * as api from "../service/TransactionService";
|
||||
|
||||
export const useGetMyCoupons = () => {
|
||||
return useQuery({
|
||||
queryKey: ["my-coupons"],
|
||||
queryFn: api.getMyCoupons,
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetUserWallet = () => {
|
||||
return useQuery({
|
||||
queryKey: ["user-wallet"],
|
||||
queryFn: api.getUserWalletBalance,
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetUserPointsBalance = () => {
|
||||
return useQuery({
|
||||
queryKey: ["user-points-balance"],
|
||||
queryFn: api.getUserPointsBalance,
|
||||
});
|
||||
};
|
||||
|
||||
export const useConvertScoreToWallet = () => {
|
||||
return useMutation({
|
||||
mutationFn: api.convertScoreToWallet,
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetTransactions = () => {
|
||||
return useQuery({
|
||||
queryKey: ["transactions"],
|
||||
queryFn: api.getTransactions,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
'use client';
|
||||
import React from 'react'
|
||||
import { useGetTransactions } from './hooks/useTransactionData';
|
||||
import { TransactionsTable } from './components/TransactionsTable';
|
||||
import LoadingOverlay from '@/components/overlays/LoadingOverlay';
|
||||
|
||||
function TransactionsReviewIndex() {
|
||||
const { data: transactionsResponse, isLoading: isLoadingTransactions } = useGetTransactions();
|
||||
|
||||
const transactions = transactionsResponse?.data || [];
|
||||
|
||||
const isLoading = isLoadingTransactions;
|
||||
|
||||
return (
|
||||
<section className='flex flex-col h-full pb-10 pt-6 gap-4 relative'>
|
||||
{isLoading && <LoadingOverlay />}
|
||||
|
||||
<h1 className='font-medium text-base'>لیست تراکنش ها</h1>
|
||||
|
||||
{/* <div className='grid grid-cols-3 gap-2 h-28'>
|
||||
<div className='w-full h-full flex flex-col items-center justify-center p-2.5 text-center bg-container rounded-xl'>
|
||||
<div className='size-8 bg-background rounded-full grid items-center'>
|
||||
<MoneySend
|
||||
className='stroke-primary justify-self-center dark:stroke-neutral-200'
|
||||
size={20}
|
||||
/>
|
||||
</div>
|
||||
<span className='text-xs2 pt-1'>مجموع واریز ها</span>
|
||||
<span className='text-xs font-medium mt-1 mb-0.5'>
|
||||
{formatPrice(stats.totalDeposits)} ریال
|
||||
</span>
|
||||
</div>
|
||||
<div className='w-full h-full flex flex-col items-center justify-center p-2.5 text-center bg-container rounded-xl'>
|
||||
<div className='size-8 bg-background rounded-full grid items-center'>
|
||||
<MoneyRecive
|
||||
className='stroke-primary justify-self-center dark:stroke-neutral-200'
|
||||
size={20}
|
||||
/>
|
||||
</div>
|
||||
<span className='text-xs2 pt-1'>مجموع برداشت ها</span>
|
||||
<span className='text-xs font-medium mt-1 mb-0.5'>
|
||||
{formatPrice(stats.totalWithdrawals)} ریال
|
||||
</span>
|
||||
</div>
|
||||
<div className='w-full h-full flex flex-col items-center justify-center p-2.5 text-center bg-container rounded-xl'>
|
||||
<div className='size-8 bg-background rounded-full grid items-center'>
|
||||
<Wallet
|
||||
className='stroke-primary justify-self-center dark:stroke-neutral-200'
|
||||
size={20}
|
||||
/>
|
||||
</div>
|
||||
<span className='text-xs2 pt-1'>موجودی کیف پول</span>
|
||||
<span className='text-xs font-medium mt-1 mb-0.5'>
|
||||
{formatPrice(walletAmount)} ریال
|
||||
</span>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div className='w-full h-full bg-container rounded-3xl mt-0'>
|
||||
<TransactionsTable transactions={transactions} />
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default TransactionsReviewIndex
|
||||
@@ -0,0 +1,38 @@
|
||||
import { api } from "@/config/axios";
|
||||
import {
|
||||
CouponsResponse,
|
||||
WalletBalanceResponse,
|
||||
TransactionsResponse,
|
||||
} from "../types/Types";
|
||||
|
||||
export const getMyCoupons = async (): Promise<CouponsResponse> => {
|
||||
const { data } = await api.get<CouponsResponse>("/public/coupons/me");
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getUserWalletBalance = async (): Promise<
|
||||
WalletBalanceResponse
|
||||
> => {
|
||||
const { data } = await api.get<WalletBalanceResponse>(
|
||||
"/public/user/wallet/balance"
|
||||
);
|
||||
return data;
|
||||
};
|
||||
export const getUserPointsBalance = async (): Promise<
|
||||
WalletBalanceResponse
|
||||
> => {
|
||||
const { data } = await api.get<WalletBalanceResponse>(
|
||||
"/public/user/points/balance"
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const convertScoreToWallet = async () => {
|
||||
const { data } = await api.post("/public/user/convert-score-to-wallet");
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getTransactions = async (): Promise<TransactionsResponse> => {
|
||||
const { data } = await api.get<TransactionsResponse>("/public/payments");
|
||||
return data;
|
||||
};
|
||||
@@ -0,0 +1,106 @@
|
||||
import { BaseResponse } from "@/app/[name]/(Main)/types/Types";
|
||||
|
||||
export interface Coupon {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
restaurant: string;
|
||||
code: string;
|
||||
name: string;
|
||||
description: string;
|
||||
type: "PERCENTAGE" | "FIXED";
|
||||
value: number;
|
||||
maxDiscount: number | null;
|
||||
minOrderAmount: number;
|
||||
maxUses: number;
|
||||
usedCount: number;
|
||||
maxUsesPerUser: number | null;
|
||||
startDate: string | null;
|
||||
endDate: string | null;
|
||||
isActive: boolean;
|
||||
foodCategories: unknown | null;
|
||||
foods: unknown | null;
|
||||
userPhone: string | null;
|
||||
}
|
||||
|
||||
export type CouponsResponse = BaseResponse<Coupon[]>;
|
||||
|
||||
export interface Wallet {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
restaurant: string;
|
||||
user: string;
|
||||
wallet: number;
|
||||
points: number;
|
||||
}
|
||||
|
||||
export type WalletResponse = BaseResponse<Wallet>;
|
||||
|
||||
export interface WalletBalance {
|
||||
balance: number;
|
||||
}
|
||||
|
||||
export type WalletBalanceResponse = BaseResponse<WalletBalance>;
|
||||
|
||||
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<Transaction[]>;
|
||||
Reference in New Issue
Block a user