invoice
This commit is contained in:
@@ -0,0 +1,46 @@
|
|||||||
|
import { Archive } from 'iconsax-react'
|
||||||
|
import { type FC, useState } from 'react'
|
||||||
|
import ModalConfrim from './ModalConfrim'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onArchive: () => void
|
||||||
|
isloading?: boolean
|
||||||
|
colorIcon?: string
|
||||||
|
title?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const ArchiveWithConfirm: FC<Props> = ({
|
||||||
|
onArchive,
|
||||||
|
isloading = false,
|
||||||
|
colorIcon,
|
||||||
|
title = 'آرشیو',
|
||||||
|
}) => {
|
||||||
|
const [isConfirm, setIsConfirm] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
title={title}
|
||||||
|
onClick={() => setIsConfirm(true)}
|
||||||
|
className="inline-flex"
|
||||||
|
>
|
||||||
|
<Archive className="size-5" color={colorIcon || '#8C90A3'} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<ModalConfrim
|
||||||
|
isOpen={isConfirm}
|
||||||
|
close={() => setIsConfirm(false)}
|
||||||
|
onConfrim={() => {
|
||||||
|
onArchive()
|
||||||
|
setIsConfirm(false)
|
||||||
|
}}
|
||||||
|
isloading={isloading}
|
||||||
|
title_header="آرشیو پیشفاکتور"
|
||||||
|
label="آیا از آرشیو کردن این پیشفاکتور مطمئن هستید؟"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ArchiveWithConfirm
|
||||||
@@ -11,7 +11,7 @@ import { getChatSenderName } from '../type/Types'
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
content: string
|
content: string
|
||||||
attachments?: ChatAttachmentType[]
|
attachments?: ChatAttachmentType[] | null
|
||||||
senderName?: string
|
senderName?: string
|
||||||
senderLabel?: string
|
senderLabel?: string
|
||||||
createdAt?: string
|
createdAt?: string
|
||||||
@@ -29,7 +29,7 @@ type Props = {
|
|||||||
|
|
||||||
const ChatMessage: FC<Props> = ({
|
const ChatMessage: FC<Props> = ({
|
||||||
content,
|
content,
|
||||||
attachments = [],
|
attachments,
|
||||||
senderName,
|
senderName,
|
||||||
senderLabel = 'پشتیبان',
|
senderLabel = 'پشتیبان',
|
||||||
createdAt,
|
createdAt,
|
||||||
@@ -44,8 +44,9 @@ const ChatMessage: FC<Props> = ({
|
|||||||
onDelete,
|
onDelete,
|
||||||
isDeleting = false,
|
isDeleting = false,
|
||||||
}) => {
|
}) => {
|
||||||
const fileAttachments = attachments.filter((a) => a.type !== 'voice')
|
const safeAttachments = attachments ?? []
|
||||||
const voiceAttachments = attachments.filter((a) => a.type === 'voice')
|
const fileAttachments = safeAttachments.filter((a) => a.type !== 'voice')
|
||||||
|
const voiceAttachments = safeAttachments.filter((a) => a.type === 'voice')
|
||||||
|
|
||||||
const handleOpenLink = async (key: string) => {
|
const handleOpenLink = async (key: string) => {
|
||||||
const url = await getPresignedUrl(key)
|
const url = await getPresignedUrl(key)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export type ChatParentMessageType = {
|
|||||||
export type ChatMessageType = {
|
export type ChatMessageType = {
|
||||||
admin?: ChatParticipantType | null
|
admin?: ChatParticipantType | null
|
||||||
user?: ChatParticipantType | null
|
user?: ChatParticipantType | null
|
||||||
attachments: ChatAttachmentType[]
|
attachments?: ChatAttachmentType[] | null
|
||||||
content: string
|
content: string
|
||||||
createdAt: string
|
createdAt: string
|
||||||
id: string
|
id: string
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const Stats: FC = () => {
|
|||||||
<StatCard
|
<StatCard
|
||||||
count={stats?.unconfirmedInvoicesCount}
|
count={stats?.unconfirmedInvoicesCount}
|
||||||
description={t('home.factureCount')}
|
description={t('home.factureCount')}
|
||||||
to={`${Paths.perfomaInvoice.list}?tab=${ProformaInvoiceStatusEnum.NOT_CONFIRMED}`}
|
to={`${Paths.perfomaInvoice.list}?tab=${ProformaInvoiceStatusEnum.PENDING}`}
|
||||||
icon={<ReceiptText
|
icon={<ReceiptText
|
||||||
size={27}
|
size={27}
|
||||||
color={COLORS.primary}
|
color={COLORS.primary}
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import { type FC, type ReactNode } from 'react';
|
import { type FC, type ReactNode, useState } from 'react';
|
||||||
import { Link, useParams } from 'react-router-dom';
|
import { Link, useNavigate, useParams } from 'react-router-dom';
|
||||||
import moment from 'moment-jalaali';
|
import moment from 'moment-jalaali';
|
||||||
import { Calendar, Edit, Profile2User, ReceiptText, WalletMoney } from 'iconsax-react';
|
import { Archive, Calendar, Edit, Profile2User, ReceiptText, WalletMoney } from 'iconsax-react';
|
||||||
import { Paths } from '@/config/Paths';
|
import { Paths } from '@/config/Paths';
|
||||||
import BackButton from '@/components/BackButton';
|
import BackButton from '@/components/BackButton';
|
||||||
import RefreshButton from '@/components/RefreshButton';
|
import RefreshButton from '@/components/RefreshButton';
|
||||||
import { useGetInvoiceDetail } from './hooks/useInvoiceData';
|
import ModalConfrim from '@/components/ModalConfrim';
|
||||||
|
import { useArchiveInvoice, useGetInvoiceDetail } from './hooks/useInvoiceData';
|
||||||
import InvoicePaymentsSection from './components/InvoicePaymentsSection';
|
import InvoicePaymentsSection from './components/InvoicePaymentsSection';
|
||||||
import { formatItemDiscountDisplay } from './utils/invoiceItem';
|
import { formatItemDiscountDisplay } from './utils/invoiceItem';
|
||||||
import type { InvoiceItemType } from './types/Types';
|
import type { InvoiceItemType } from './types/Types';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { extractErrorMessage } from '@/config/func';
|
||||||
|
|
||||||
const formatAmount = (amount: number) =>
|
const formatAmount = (amount: number) =>
|
||||||
Number(amount).toLocaleString('fa-IR') + ' ریال';
|
Number(amount).toLocaleString('fa-IR') + ' ریال';
|
||||||
@@ -110,9 +113,26 @@ const InvoiceItemTableRow: FC<{ item: InvoiceItemType }> = ({ item }) => {
|
|||||||
|
|
||||||
const DetailPerfomaInvoice: FC = () => {
|
const DetailPerfomaInvoice: FC = () => {
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [isArchiveConfirmOpen, setIsArchiveConfirmOpen] = useState(false);
|
||||||
const { data: invoiceData, isLoading, refetch, isFetching, isError } = useGetInvoiceDetail(id);
|
const { data: invoiceData, isLoading, refetch, isFetching, isError } = useGetInvoiceDetail(id);
|
||||||
|
const { mutate: archiveInvoice, isPending: isArchiving } = useArchiveInvoice();
|
||||||
const invoice = invoiceData?.data;
|
const invoice = invoiceData?.data;
|
||||||
|
|
||||||
|
const handleArchive = () => {
|
||||||
|
if (!id) return;
|
||||||
|
archiveInvoice(id, {
|
||||||
|
onSuccess: () => {
|
||||||
|
setIsArchiveConfirmOpen(false);
|
||||||
|
toast.success('پیشفاکتور با موفقیت آرشیو شد');
|
||||||
|
navigate(Paths.perfomaInvoice.list);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(extractErrorMessage(error));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="mt-5 flex min-h-[200px] items-center justify-center">
|
<div className="mt-5 flex min-h-[200px] items-center justify-center">
|
||||||
@@ -154,18 +174,44 @@ const DetailPerfomaInvoice: FC = () => {
|
|||||||
|
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<RefreshButton onClick={() => refetch()} isLoading={isFetching} />
|
<RefreshButton onClick={() => refetch()} isLoading={isFetching} />
|
||||||
<Link to={Paths.perfomaInvoice.update + invoice.id}>
|
{invoice.status !== 'archived' && (
|
||||||
<button
|
<>
|
||||||
type="button"
|
<button
|
||||||
className="flex items-center gap-2 rounded-xl bg-primary px-5 py-2.5 text-sm font-medium hover:opacity-90"
|
type="button"
|
||||||
>
|
onClick={() => setIsArchiveConfirmOpen(true)}
|
||||||
<Edit size={18} color="black" />
|
className="flex items-center gap-2 rounded-xl border border-border px-5 py-2.5 text-sm font-medium text-[#4B5563] transition-colors hover:bg-gray-50"
|
||||||
ویرایش
|
>
|
||||||
</button>
|
<Archive size={18} color="#4B5563" />
|
||||||
</Link>
|
آرشیو
|
||||||
|
</button>
|
||||||
|
<Link to={Paths.perfomaInvoice.update + invoice.id}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="flex items-center gap-2 rounded-xl bg-primary px-5 py-2.5 text-sm font-medium hover:opacity-90"
|
||||||
|
>
|
||||||
|
<Edit size={18} color="black" />
|
||||||
|
ویرایش
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{invoice.status === 'archived' && (
|
||||||
|
<span className="inline-flex items-center rounded-full bg-gray-100 px-3 py-1.5 text-xs text-gray-700">
|
||||||
|
آرشیو شده
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ModalConfrim
|
||||||
|
isOpen={isArchiveConfirmOpen}
|
||||||
|
close={() => setIsArchiveConfirmOpen(false)}
|
||||||
|
onConfrim={handleArchive}
|
||||||
|
isloading={isArchiving}
|
||||||
|
title_header="آرشیو پیشفاکتور"
|
||||||
|
label="آیا از آرشیو کردن این پیشفاکتور مطمئن هستید؟"
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-6 xl:grid-cols-[minmax(0,1fr)_320px]">
|
<div className="grid grid-cols-1 gap-6 xl:grid-cols-[minmax(0,1fr)_320px]">
|
||||||
<div className="rounded-3xl border border-border/70 bg-white p-6 shadow-sm">
|
<div className="rounded-3xl border border-border/70 bg-white p-6 shadow-sm">
|
||||||
<div className="font-medium text-[#111827]">اطلاعات پیش فاکتور</div>
|
<div className="font-medium text-[#111827]">اطلاعات پیش فاکتور</div>
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import Tabs from '@/components/Tabs'
|
import Tabs from '@/components/Tabs'
|
||||||
import { useMemo, useState, type FC } from 'react'
|
import { useCallback, useMemo, useState, type FC } from 'react'
|
||||||
import { ProformaInvoiceStatusEnum } from './enum/InvoiceEnum'
|
import {
|
||||||
|
invoiceListTabs,
|
||||||
|
invoiceListTabStatuses,
|
||||||
|
ProformaInvoiceStatusEnum,
|
||||||
|
} from './enum/InvoiceEnum'
|
||||||
import Filters from '@/components/Filters'
|
import Filters from '@/components/Filters'
|
||||||
import RefreshButton from '@/components/RefreshButton'
|
import RefreshButton from '@/components/RefreshButton'
|
||||||
import Table from '@/components/Table'
|
import Table from '@/components/Table'
|
||||||
@@ -9,15 +13,13 @@ import { Link, useSearchParams } from 'react-router-dom'
|
|||||||
import { Paths } from '@/config/Paths'
|
import { Paths } from '@/config/Paths'
|
||||||
import moment from 'moment-jalaali'
|
import moment from 'moment-jalaali'
|
||||||
import type { FilterValues } from '@/components/Filters'
|
import type { FilterValues } from '@/components/Filters'
|
||||||
import { useGetInvoice } from './hooks/useInvoiceData'
|
import { useArchiveInvoice, useGetInvoice } from './hooks/useInvoiceData'
|
||||||
import type { InvoiceType } from './types/Types'
|
import type { InvoiceType } from './types/Types'
|
||||||
|
import ArchiveWithConfirm from '@/components/ArchiveWithConfirm'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
import { extractErrorMessage } from '@/config/func'
|
||||||
|
|
||||||
const INVOICE_TABS: ProformaInvoiceStatusEnum[] = [
|
const INVOICE_TABS = invoiceListTabs.map((tab) => tab.value)
|
||||||
ProformaInvoiceStatusEnum.ALL,
|
|
||||||
ProformaInvoiceStatusEnum.NOT_CONFIRMED,
|
|
||||||
ProformaInvoiceStatusEnum.PARTIALLY_CONFIRMED,
|
|
||||||
ProformaInvoiceStatusEnum.CONFIRMED,
|
|
||||||
]
|
|
||||||
|
|
||||||
const isValidInvoiceTab = (value: string | null): value is ProformaInvoiceStatusEnum =>
|
const isValidInvoiceTab = (value: string | null): value is ProformaInvoiceStatusEnum =>
|
||||||
value !== null && INVOICE_TABS.includes(value as ProformaInvoiceStatusEnum)
|
value !== null && INVOICE_TABS.includes(value as ProformaInvoiceStatusEnum)
|
||||||
@@ -25,43 +27,48 @@ const isValidInvoiceTab = (value: string | null): value is ProformaInvoiceStatus
|
|||||||
const ProformaInvoice: FC = () => {
|
const ProformaInvoice: FC = () => {
|
||||||
const [searchParams, setSearchParams] = useSearchParams()
|
const [searchParams, setSearchParams] = useSearchParams()
|
||||||
const tabParam = searchParams.get('tab')
|
const tabParam = searchParams.get('tab')
|
||||||
const activeTab = isValidInvoiceTab(tabParam) ? tabParam : ProformaInvoiceStatusEnum.ALL
|
const activeTab = isValidInvoiceTab(tabParam)
|
||||||
|
? tabParam
|
||||||
|
: ProformaInvoiceStatusEnum.PENDING
|
||||||
const [page, setPage] = useState(1)
|
const [page, setPage] = useState(1)
|
||||||
const [filters, setFilters] = useState<FilterValues>({})
|
const [filters, setFilters] = useState<FilterValues>({})
|
||||||
|
|
||||||
|
const statuses = invoiceListTabStatuses[activeTab]
|
||||||
|
|
||||||
const setActiveTab = (tab: ProformaInvoiceStatusEnum) => {
|
const setActiveTab = (tab: ProformaInvoiceStatusEnum) => {
|
||||||
|
setPage(1)
|
||||||
setSearchParams(
|
setSearchParams(
|
||||||
(current) => {
|
(current) => {
|
||||||
const next = new URLSearchParams(current)
|
const next = new URLSearchParams(current)
|
||||||
if (tab === ProformaInvoiceStatusEnum.ALL) {
|
next.set('tab', tab)
|
||||||
next.delete('tab')
|
|
||||||
} else {
|
|
||||||
next.set('tab', tab)
|
|
||||||
}
|
|
||||||
return next
|
return next
|
||||||
},
|
},
|
||||||
{ replace: true },
|
{ replace: true },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data, isLoading, refetch, isFetching } = useGetInvoice(page)
|
const { data, isLoading, refetch, isFetching } = useGetInvoice({ page, statuses })
|
||||||
|
const { mutate: archiveInvoice, isPending: isArchiving } = useArchiveInvoice()
|
||||||
|
|
||||||
const meta = data?.meta
|
const meta = data?.meta
|
||||||
|
|
||||||
|
const handleArchive = useCallback(
|
||||||
|
(id: string) => {
|
||||||
|
archiveInvoice(id, {
|
||||||
|
onSuccess: () => {
|
||||||
|
refetch()
|
||||||
|
toast.success('پیشفاکتور با موفقیت آرشیو شد')
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(extractErrorMessage(error))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[archiveInvoice, refetch],
|
||||||
|
)
|
||||||
|
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
const list = data?.data ?? []
|
let result = data?.data ?? []
|
||||||
|
|
||||||
if (activeTab === ProformaInvoiceStatusEnum.CONFIRMED) {
|
|
||||||
return list.filter((o) => o.confirmStatus === 'confirmed')
|
|
||||||
}
|
|
||||||
if (activeTab === ProformaInvoiceStatusEnum.PARTIALLY_CONFIRMED) {
|
|
||||||
return list.filter((o) => o.confirmStatus === 'partially_confirmed')
|
|
||||||
}
|
|
||||||
if (activeTab === ProformaInvoiceStatusEnum.NOT_CONFIRMED) {
|
|
||||||
return list.filter((o) => o.confirmStatus === 'pending')
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = list
|
|
||||||
|
|
||||||
const search = (filters.search ?? '').trim()
|
const search = (filters.search ?? '').trim()
|
||||||
if (search) {
|
if (search) {
|
||||||
@@ -85,7 +92,7 @@ const ProformaInvoice: FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}, [data?.data, activeTab, filters.search, filters.date])
|
}, [data?.data, filters.search, filters.date])
|
||||||
|
|
||||||
const getPaymentStatus = (item: InvoiceType) => {
|
const getPaymentStatus = (item: InvoiceType) => {
|
||||||
const total = Number(item.total) || 0
|
const total = Number(item.total) || 0
|
||||||
@@ -127,12 +134,7 @@ const ProformaInvoice: FC = () => {
|
|||||||
|
|
||||||
<div className='mt-8'>
|
<div className='mt-8'>
|
||||||
<Tabs
|
<Tabs
|
||||||
items={[
|
items={[...invoiceListTabs]}
|
||||||
{ label: 'همه', value: ProformaInvoiceStatusEnum.ALL },
|
|
||||||
{ label: 'تایید نشده', value: ProformaInvoiceStatusEnum.NOT_CONFIRMED },
|
|
||||||
{ label: 'تایید جزئی', value: ProformaInvoiceStatusEnum.PARTIALLY_CONFIRMED },
|
|
||||||
{ label: 'تایید شده', value: ProformaInvoiceStatusEnum.CONFIRMED },
|
|
||||||
]}
|
|
||||||
activeTab={activeTab}
|
activeTab={activeTab}
|
||||||
onTabChange={(tab) => setActiveTab(tab as ProformaInvoiceStatusEnum)}
|
onTabChange={(tab) => setActiveTab(tab as ProformaInvoiceStatusEnum)}
|
||||||
/>
|
/>
|
||||||
@@ -195,15 +197,16 @@ const ProformaInvoice: FC = () => {
|
|||||||
: '-',
|
: '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'confirmStatus',
|
key: 'status',
|
||||||
title: 'وضعیت تایید',
|
title: 'وضعیت تایید',
|
||||||
render: (item) => {
|
render: (item) => {
|
||||||
const statusMap = {
|
const statusMap = {
|
||||||
confirmed: { className: 'bg-green-100 text-green-800', text: 'تایید شده' },
|
confirmed: { className: 'bg-green-100 text-green-800', text: 'تایید شده' },
|
||||||
partially_confirmed: { className: 'bg-blue-100 text-blue-800', text: 'تایید جزئی' },
|
partially_confirmed: { className: 'bg-blue-100 text-blue-800', text: 'تایید جزئی' },
|
||||||
pending: { className: 'bg-amber-100 text-amber-800', text: 'تایید نشده' },
|
pending: { className: 'bg-amber-100 text-amber-800', text: 'در انتظار تایید' },
|
||||||
|
archived: { className: 'bg-gray-100 text-gray-700', text: 'آرشیو شده' },
|
||||||
}
|
}
|
||||||
const status = statusMap[item.confirmStatus] ?? statusMap.pending
|
const status = statusMap[item.status] ?? statusMap.pending
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={`inline-flex h-6 items-center rounded-full px-2.5 text-xs ${status.className}`}
|
className={`inline-flex h-6 items-center rounded-full px-2.5 text-xs ${status.className}`}
|
||||||
@@ -226,9 +229,17 @@ const ProformaInvoice: FC = () => {
|
|||||||
<Link to={Paths.perfomaInvoice.detail + item.id}>
|
<Link to={Paths.perfomaInvoice.detail + item.id}>
|
||||||
<Eye size={20} color='#8C90A3' />
|
<Eye size={20} color='#8C90A3' />
|
||||||
</Link>
|
</Link>
|
||||||
<Link to={Paths.perfomaInvoice.update + item.id}>
|
{item.status !== 'archived' && (
|
||||||
<Edit2 size={20} color='#0037FF' />
|
<>
|
||||||
</Link>
|
<Link to={Paths.perfomaInvoice.update + item.id}>
|
||||||
|
<Edit2 size={20} color='#0037FF' />
|
||||||
|
</Link>
|
||||||
|
<ArchiveWithConfirm
|
||||||
|
onArchive={() => handleArchive(item.id)}
|
||||||
|
isloading={isArchiving}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -251,4 +262,4 @@ const ProformaInvoice: FC = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ProformaInvoice
|
export default ProformaInvoice
|
||||||
|
|||||||
@@ -1,6 +1,26 @@
|
|||||||
export const enum ProformaInvoiceStatusEnum {
|
export const enum ProformaInvoiceStatusEnum {
|
||||||
ALL = 'all',
|
PENDING = 'pending',
|
||||||
NOT_CONFIRMED = 'not_confirmed',
|
|
||||||
PARTIALLY_CONFIRMED = 'partially_confirmed',
|
|
||||||
CONFIRMED = 'confirmed',
|
CONFIRMED = 'confirmed',
|
||||||
}
|
ARCHIVED = 'archived',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InvoiceConfirmStatus =
|
||||||
|
| 'pending'
|
||||||
|
| 'partially_confirmed'
|
||||||
|
| 'confirmed'
|
||||||
|
| 'archived'
|
||||||
|
|
||||||
|
export const invoiceListTabStatuses: Record<
|
||||||
|
ProformaInvoiceStatusEnum,
|
||||||
|
InvoiceConfirmStatus[]
|
||||||
|
> = {
|
||||||
|
[ProformaInvoiceStatusEnum.PENDING]: ['pending'],
|
||||||
|
[ProformaInvoiceStatusEnum.CONFIRMED]: ['partially_confirmed', 'confirmed'],
|
||||||
|
[ProformaInvoiceStatusEnum.ARCHIVED]: ['archived'],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const invoiceListTabs = [
|
||||||
|
{ label: 'در انتظار تایید', value: ProformaInvoiceStatusEnum.PENDING },
|
||||||
|
{ label: 'تایید شده', value: ProformaInvoiceStatusEnum.CONFIRMED },
|
||||||
|
{ label: 'آرشیو شده', value: ProformaInvoiceStatusEnum.ARCHIVED },
|
||||||
|
] as const
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ export const useCreateInvoice = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetInvoice = (page = 1) => {
|
export const useGetInvoice = (params: api.GetInvoicesParams) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["invoice", page],
|
queryKey: ["invoice", params.page, params.statuses?.join(",") ?? ""],
|
||||||
queryFn: () => api.getInvoice(page),
|
queryFn: () => api.getInvoice(params),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,3 +52,14 @@ export const useUpdateInvoice = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useArchiveInvoice = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (id: string) => api.archiveInvoice(id),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["invoice"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -6,15 +6,27 @@ import type {
|
|||||||
InvoiceType,
|
InvoiceType,
|
||||||
UpdatePreInvoiceType,
|
UpdatePreInvoiceType,
|
||||||
} from "../types/Types";
|
} from "../types/Types";
|
||||||
|
import type { InvoiceConfirmStatus } from "../enum/InvoiceEnum";
|
||||||
|
|
||||||
|
export type GetInvoicesParams = {
|
||||||
|
page?: number;
|
||||||
|
statuses?: InvoiceConfirmStatus[];
|
||||||
|
};
|
||||||
|
|
||||||
export const createInvoice = async (params: CreatePreInvoiceType) => {
|
export const createInvoice = async (params: CreatePreInvoiceType) => {
|
||||||
const { data } = await axios.post("/admin/invoice", params);
|
const { data } = await axios.post("/admin/invoice", params);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getInvoice = async (page = 1): Promise<GetInvoiceResponseType> => {
|
export const getInvoice = async (
|
||||||
|
params: GetInvoicesParams = {},
|
||||||
|
): Promise<GetInvoiceResponseType> => {
|
||||||
|
const { page = 1, statuses } = params;
|
||||||
const { data } = await axios.get<GetInvoiceResponseType>("/admin/invoice", {
|
const { data } = await axios.get<GetInvoiceResponseType>("/admin/invoice", {
|
||||||
params: { page },
|
params: {
|
||||||
|
page,
|
||||||
|
...(statuses?.length ? { statuses: statuses.join(",") } : {}),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
@@ -35,3 +47,8 @@ export const updateInvoice = async (id: string, params: UpdatePreInvoiceType) =>
|
|||||||
const { data } = await axios.patch(`/admin/invoice/${id}`, params);
|
const { data } = await axios.patch(`/admin/invoice/${id}`, params);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const archiveInvoice = async (id: string) => {
|
||||||
|
const { data } = await axios.patch(`/admin/invoice/${id}/archive`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export type InvoiceType = {
|
|||||||
total: number;
|
total: number;
|
||||||
paidAmount: number;
|
paidAmount: number;
|
||||||
balance: number;
|
balance: number;
|
||||||
confirmStatus: 'pending' | 'partially_confirmed' | 'confirmed';
|
status: 'pending' | 'partially_confirmed' | 'confirmed' | 'archived';
|
||||||
enableTax: boolean;
|
enableTax: boolean;
|
||||||
approvalDeadline: string;
|
approvalDeadline: string;
|
||||||
attachments: InvoiceAttachmentType[];
|
attachments: InvoiceAttachmentType[];
|
||||||
|
|||||||
Reference in New Issue
Block a user