208 lines
8.4 KiB
TypeScript
208 lines
8.4 KiB
TypeScript
import Tabs from '@/components/Tabs'
|
||
import { useMemo, useState, type FC } from 'react'
|
||
import { ProformaInvoiceStatusEnum } from './enum/InvoiceEnum'
|
||
import Filters from '@/components/Filters'
|
||
import Table from '@/components/Table'
|
||
import { Eye, Add } from 'iconsax-react'
|
||
import { Link } from 'react-router-dom'
|
||
import { Paths } from '@/config/Paths'
|
||
import moment from 'moment-jalaali'
|
||
import type { FilterValues } from '@/components/Filters'
|
||
import { useGetInvoice } from './hooks/useInvoiceData'
|
||
import type { InvoiceType } from './types/Types'
|
||
|
||
const isInvoiceConfirmed = (item: InvoiceType) => {
|
||
const items = item.items ?? []
|
||
return items.length > 0 && items.every((i) => !!i.confirmedAt)
|
||
}
|
||
|
||
const ProformaInvoice: FC = () => {
|
||
const [activeTab, setActiveTab] = useState<ProformaInvoiceStatusEnum>(ProformaInvoiceStatusEnum.ALL)
|
||
const [page, setPage] = useState(1)
|
||
const [filters, setFilters] = useState<FilterValues>({})
|
||
|
||
const { data, isLoading } = useGetInvoice(page)
|
||
|
||
const meta = data?.meta
|
||
|
||
const filteredData = useMemo(() => {
|
||
const list = data?.data ?? []
|
||
|
||
if (activeTab === ProformaInvoiceStatusEnum.CONFIRMED) {
|
||
return list.filter((o) => isInvoiceConfirmed(o))
|
||
}
|
||
if (activeTab === ProformaInvoiceStatusEnum.NOT_CONFIRMED) {
|
||
return list.filter((o) => !isInvoiceConfirmed(o))
|
||
}
|
||
|
||
let result = list
|
||
|
||
const search = (filters.search ?? '').trim()
|
||
if (search) {
|
||
const lower = search.toLowerCase()
|
||
result = result.filter(
|
||
(o) =>
|
||
String(o.invoiceNumber).includes(search) ||
|
||
o.user?.firstName?.toLowerCase().includes(lower) ||
|
||
o.user?.lastName?.toLowerCase().includes(lower) ||
|
||
o.user?.phone?.includes(search)
|
||
)
|
||
}
|
||
|
||
const dateFilter = filters.date
|
||
if (dateFilter) {
|
||
const filterDate = moment(dateFilter, 'jYYYY/jMM/jDD').format('YYYY-MM-DD')
|
||
result = result.filter((o) => {
|
||
const d = o.createdAt
|
||
return d && moment(d).format('YYYY-MM-DD') === filterDate
|
||
})
|
||
}
|
||
|
||
return result
|
||
}, [data?.data, activeTab, filters.search, filters.date])
|
||
|
||
const getPaymentStatus = (item: InvoiceType) => {
|
||
const total = Number(item.total) || 0
|
||
const paid = Number(item.paidAmount) || 0
|
||
if (total <= 0) return '-'
|
||
if (paid >= total) return 'پرداخت شده'
|
||
if (paid > 0) return 'پرداخت جزئی'
|
||
return 'پرداخت نشده'
|
||
}
|
||
|
||
const getServiceLabel = (item: InvoiceType) => {
|
||
if (!item.items?.length) return '-'
|
||
if (item.items.length === 1) return item.items[0].product?.title ?? '-'
|
||
return `${item.items.length} خدمت`
|
||
}
|
||
|
||
return (
|
||
<div className='mt-5'>
|
||
<div className='flex justify-between items-center'>
|
||
<h1 className='text-lg font-light'>پیش فاکتورها</h1>
|
||
<Link to={Paths.perfomaInvoice.create}>
|
||
<button className='flex items-center gap-2 px-4 py-2 bg-primary text-black text-sm font-medium rounded-xl hover:opacity-90 transition-opacity'>
|
||
<Add size={18} />
|
||
پیش فاکتور جدید
|
||
</button>
|
||
</Link>
|
||
</div>
|
||
|
||
<div className='mt-8'>
|
||
<Tabs
|
||
items={[
|
||
{ label: 'همه', value: ProformaInvoiceStatusEnum.ALL },
|
||
{ label: 'تایید نشده', value: ProformaInvoiceStatusEnum.NOT_CONFIRMED },
|
||
{ label: 'تایید شده', value: ProformaInvoiceStatusEnum.CONFIRMED },
|
||
]}
|
||
activeTab={activeTab}
|
||
onTabChange={(tab) => setActiveTab(tab as ProformaInvoiceStatusEnum)}
|
||
/>
|
||
</div>
|
||
|
||
<div className='mt-8'>
|
||
<Filters
|
||
fields={[
|
||
{
|
||
type: 'input',
|
||
name: 'search',
|
||
placeholder: 'جستجو (شماره سفارش / مشتری)',
|
||
},
|
||
{
|
||
type: 'date',
|
||
name: 'date',
|
||
placeholder: 'تاریخ',
|
||
},
|
||
]}
|
||
onChange={setFilters}
|
||
/>
|
||
</div>
|
||
|
||
<div className='mt-8'>
|
||
<Table
|
||
columns={[
|
||
{
|
||
key: 'invoiceNumber',
|
||
title: 'شماره',
|
||
render: (item) => item.invoiceNumber,
|
||
},
|
||
{
|
||
key: 'service',
|
||
title: 'خدمت',
|
||
render: (item) => getServiceLabel(item),
|
||
},
|
||
{
|
||
key: 'issueDate',
|
||
title: 'تاریخ صدور',
|
||
render: (item) =>
|
||
item.createdAt
|
||
? moment(item.createdAt).format('jYYYY/jMM/jDD')
|
||
: '-',
|
||
},
|
||
{
|
||
key: 'lastConfirmDate',
|
||
title: 'آخرین مهلت تایید',
|
||
render: (item) =>
|
||
item.approvalDeadline
|
||
? moment(item.approvalDeadline).format('jYYYY/jMM/jDD')
|
||
: '-',
|
||
},
|
||
{
|
||
key: 'total',
|
||
title: 'مجموع',
|
||
render: (item) =>
|
||
item.total
|
||
? Number(item.total).toLocaleString('fa-IR') + ' تومان'
|
||
: '-',
|
||
},
|
||
{
|
||
key: 'confirmStatus',
|
||
title: 'وضعیت تایید',
|
||
render: (item) => {
|
||
const confirmed = isInvoiceConfirmed(item)
|
||
return (
|
||
<span
|
||
className={`inline-flex h-6 items-center rounded-full px-2.5 text-xs ${confirmed ? 'bg-green-100 text-green-800' : 'bg-amber-100 text-amber-800'
|
||
}`}
|
||
>
|
||
{confirmed ? 'تایید شده' : 'تایید نشده'}
|
||
</span>
|
||
)
|
||
},
|
||
},
|
||
{
|
||
key: 'paymentStatus',
|
||
title: 'وضعیت پرداخت',
|
||
render: (item) => getPaymentStatus(item),
|
||
},
|
||
{
|
||
key: 'actions',
|
||
title: '',
|
||
render: (item) => (
|
||
<div className='flex gap-2 items-center'>
|
||
<Link to={Paths.perfomaInvoice.update + item.id}>
|
||
<Eye size={20} color='#8C90A3' />
|
||
</Link>
|
||
</div>
|
||
),
|
||
},
|
||
]}
|
||
data={filteredData}
|
||
isLoading={isLoading}
|
||
noDataMessage='پیش فاکتوری یافت نشد.'
|
||
pagination={
|
||
meta && meta.totalPages > 1
|
||
? {
|
||
currentPage: meta.page,
|
||
totalPages: meta.totalPages,
|
||
onPageChange: setPage,
|
||
}
|
||
: undefined
|
||
}
|
||
/>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default ProformaInvoice |