update order page

This commit is contained in:
2026-07-02 19:41:27 +03:30
parent 141f70c6f8
commit b76b4704de
6 changed files with 130 additions and 24 deletions
@@ -92,7 +92,6 @@ const DetailPerfomaInvoice: FC = () => {
const lineTotal = item.total ?? 0; const lineTotal = item.total ?? 0;
const convertToOrderUrl = `${Paths.convertToOrder}?${new URLSearchParams({ const convertToOrderUrl = `${Paths.convertToOrder}?${new URLSearchParams({
invoiceItemId: item.id, invoiceItemId: item.id,
productId: item.product?.id ?? '',
}).toString()}`; }).toString()}`;
return ( return (
<tr key={item.id} className="border-b border-border/50"> <tr key={item.id} className="border-b border-border/50">
@@ -28,6 +28,14 @@ export const useGetInvoiceDetail = (id: string | undefined) => {
}); });
}; };
export const useGetInvoiceItem = (id: string | null) => {
return useQuery({
queryKey: ["invoice-item", id],
queryFn: () => api.getInvoiceItemById(id!),
enabled: !!id,
});
};
export const useUpdateInvoice = () => { export const useUpdateInvoice = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@@ -2,6 +2,7 @@ import axios from "@/config/axios";
import type { import type {
CreatePreInvoiceType, CreatePreInvoiceType,
GetInvoiceResponseType, GetInvoiceResponseType,
InvoiceItemDetailType,
InvoiceType, InvoiceType,
UpdatePreInvoiceType, UpdatePreInvoiceType,
} from "../types/Types"; } from "../types/Types";
@@ -23,6 +24,13 @@ export const getInvoiceById = async (id: string) => {
return data; return data;
}; };
export const getInvoiceItemById = async (id: string) => {
const { data } = await axios.get<{ data: InvoiceItemDetailType }>(
`/admin/invoice/item/${id}`,
);
return data;
};
export const updateInvoice = async (id: string, params: UpdatePreInvoiceType) => { 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;
+4
View File
@@ -97,6 +97,10 @@ export type InvoiceItemType = {
confirmedAt: string | null; confirmedAt: string | null;
}; };
export type InvoiceItemDetailType = Omit<InvoiceItemType, 'invoice'> & {
invoice: Pick<InvoiceType, 'id' | 'invoiceNumber' | 'user'>;
};
export type InvoiceType = { export type InvoiceType = {
id: string; id: string;
createdAt: string; createdAt: string;
+100 -9
View File
@@ -1,6 +1,8 @@
import { useState, type FC } from 'react' import { useState, type FC } from 'react'
import { useFormik } from 'formik' import { useFormik } from 'formik'
import * as Yup from 'yup' import * as Yup from 'yup'
import moment from 'moment-jalaali'
import { Link, useNavigate, useSearchParams } from 'react-router-dom'
import Input from '@/components/Input' import Input from '@/components/Input'
import Textarea from '@/components/Textarea' import Textarea from '@/components/Textarea'
import Select from '@/components/Select' import Select from '@/components/Select'
@@ -11,18 +13,19 @@ import type { ConvertToOrderItemType } from './types/Types'
import { useConverToOrder, useGetProducts } from './hooks/useOrderData' import { useConverToOrder, useGetProducts } from './hooks/useOrderData'
import { useGetCategory } from '@/pages/product/hooks/useProductData' import { useGetCategory } from '@/pages/product/hooks/useProductData'
import { useGetUsers } from '@/pages/user/hooks/useUserData' import { useGetUsers } from '@/pages/user/hooks/useUserData'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { Paths } from '@/config/Paths' import { Paths } from '@/config/Paths'
import { extractErrorMessage } from '@/config/func' import { extractErrorMessage } from '@/config/func'
import { useGetAdmins } from '../admin/hooks/useAdminData' import { useGetAdmins } from '../admin/hooks/useAdminData'
import { useGetInvoiceItem } from '@/pages/invoice/hooks/useInvoiceData'
import { formatItemDiscountDisplay } from '@/pages/invoice/utils/invoiceItem'
const getInitialValues = ( const formatAmount = (amount: number) =>
invoiceItemId: string | null, Number(amount).toLocaleString('fa-IR') + ' ریال'
productId: string | null
): ConvertToOrderItemType => ({ const getInitialValues = (invoiceItemId: string | null): ConvertToOrderItemType => ({
invoiceItemId: invoiceItemId ?? '', invoiceItemId: invoiceItemId ?? '',
productId: productId ?? '', productId: '',
userId: '', userId: '',
typeId: '', typeId: '',
estimatedDays: 0, estimatedDays: 0,
@@ -49,18 +52,19 @@ const ConvertToOrders: FC = () => {
const navigate = useNavigate() const navigate = useNavigate()
const [searchParams] = useSearchParams() const [searchParams] = useSearchParams()
const invoiceItemId = searchParams.get('invoiceItemId') const invoiceItemId = searchParams.get('invoiceItemId')
const productId = searchParams.get('productId')
const multiUpload = useMultiUpload() const multiUpload = useMultiUpload()
const [attachmentFiles, setAttachmentFiles] = useState<File[]>([]) const [attachmentFiles, setAttachmentFiles] = useState<File[]>([])
const { data: categories } = useGetCategory() const { data: categories } = useGetCategory()
const { data: users } = useGetUsers() const { data: users } = useGetUsers()
const { data: products } = useGetProducts() const { data: products } = useGetProducts()
const { data: admins } = useGetAdmins(1, 50) const { data: admins } = useGetAdmins(1, 50)
const { data: invoiceItemData, isLoading: isInvoiceItemLoading } = useGetInvoiceItem(invoiceItemId)
const invoiceItem = invoiceItemData?.data
const { mutate: converToOrder, isPending } = useConverToOrder() const { mutate: converToOrder, isPending } = useConverToOrder()
const formik = useFormik<ConvertToOrderItemType>({ const formik = useFormik<ConvertToOrderItemType>({
initialValues: getInitialValues(invoiceItemId, productId), initialValues: getInitialValues(invoiceItemId),
validationSchema: getValidationSchema(invoiceItemId), validationSchema: getValidationSchema(invoiceItemId),
enableReinitialize: true, enableReinitialize: true,
onSubmit: async (values) => { onSubmit: async (values) => {
@@ -76,7 +80,9 @@ const ConvertToOrders: FC = () => {
const payload: ConvertToOrderItemType = { const payload: ConvertToOrderItemType = {
...values, ...values,
invoiceItemId: invoiceItemId ?? values.invoiceItemId ?? '', invoiceItemId: invoiceItemId ?? values.invoiceItemId ?? '',
productId: values.productId || undefined, productId: invoiceItemId
? invoiceItem?.product?.id
: values.productId || undefined,
userId: invoiceItemId ? undefined : values.userId, userId: invoiceItemId ? undefined : values.userId,
estimatedDays: Number(values.estimatedDays), estimatedDays: Number(values.estimatedDays),
description: values.description || undefined, description: values.description || undefined,
@@ -95,10 +101,95 @@ const ConvertToOrders: FC = () => {
}, },
}) })
const customerName =
invoiceItem?.invoice?.user?.firstName && invoiceItem?.invoice?.user?.lastName
? `${invoiceItem.invoice.user.firstName} ${invoiceItem.invoice.user.lastName}`
: invoiceItem?.invoice?.user?.phone ?? '-'
const discounts = invoiceItem ? formatItemDiscountDisplay(invoiceItem) : null
return ( return (
<div className="mt-5"> <div className="mt-5">
<h1 className="text-lg font-light">تبدیل به سفارش</h1> <h1 className="text-lg font-light">تبدیل به سفارش</h1>
{invoiceItemId && (
<div className="mt-8 rounded-3xl bg-white p-6">
<div className="font-light">اطلاعات آیتم پیش فاکتور</div>
{isInvoiceItemLoading ? (
<div className="mt-6 flex min-h-[120px] items-center justify-center text-[#888888]">
در حال بارگذاری...
</div>
) : invoiceItem ? (
<div className="mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
<div>
<div className="mb-1.5 text-[13px] text-[#888888]">پیش فاکتور</div>
<Link
to={Paths.perfomaInvoice.detail + invoiceItem.invoice.id}
className="text-[15px] text-primary hover:underline"
>
#{invoiceItem.invoice.invoiceNumber}
</Link>
</div>
<div>
<div className="mb-1.5 text-[13px] text-[#888888]">مشتری</div>
<div className="text-[15px]">{customerName}</div>
</div>
<div>
<div className="mb-1.5 text-[13px] text-[#888888]">محصول</div>
<div className="text-[15px]">{invoiceItem.product?.title ?? '-'}</div>
</div>
<div>
<div className="mb-1.5 text-[13px] text-[#888888]">تعداد</div>
<div className="text-[15px]">{invoiceItem.quantity ?? '-'}</div>
</div>
<div>
<div className="mb-1.5 text-[13px] text-[#888888]">مبلغ واحد</div>
<div className="text-[15px]">
{invoiceItem.unitPrice != null
? formatAmount(invoiceItem.unitPrice)
: '-'}
</div>
</div>
<div>
<div className="mb-1.5 text-[13px] text-[#888888]">تخفیف (مبلغ)</div>
<div className="text-[15px]">{discounts?.value ?? '-'}</div>
</div>
<div>
<div className="mb-1.5 text-[13px] text-[#888888]">تخفیف (درصد)</div>
<div className="text-[15px]">{discounts?.percent ?? '-'}</div>
</div>
<div>
<div className="mb-1.5 text-[13px] text-[#888888]">مبلغ کل</div>
<div className="text-[15px] font-semibold">
{formatAmount(invoiceItem.total ?? 0)}
</div>
</div>
<div>
<div className="mb-1.5 text-[13px] text-[#888888]">تاریخ تایید</div>
<div className="text-[15px]">
{invoiceItem.confirmedAt
? moment(invoiceItem.confirmedAt).format('jYYYY/jMM/jDD HH:mm')
: '-'}
</div>
</div>
{invoiceItem.description && (
<div className="sm:col-span-2 lg:col-span-3">
<div className="mb-1.5 text-[13px] text-[#888888]">توضیحات</div>
<div className="whitespace-pre-wrap text-[15px]">
{invoiceItem.description}
</div>
</div>
)}
</div>
) : (
<div className="mt-6 text-[15px] text-red-500">
آیتم پیش فاکتور یافت نشد.
</div>
)}
</div>
)}
<div className="bg-white rounded-3xl p-6 mt-8"> <div className="bg-white rounded-3xl p-6 mt-8">
<form <form
onSubmit={formik.handleSubmit} onSubmit={formik.handleSubmit}
+10 -14
View File
@@ -1,7 +1,7 @@
import Filters, { type FilterValues } from '@/components/Filters' import Filters, { type FilterValues } from '@/components/Filters'
import { type FC, useState } from 'react' import { type FC, useState } from 'react'
import Table from '@/components/Table' import Table from '@/components/Table'
import { AddSquare, Edit2, Eye, Printer, Receipt21 } from 'iconsax-react' import { AddSquare, Edit2, Printer, Receipt21 } from 'iconsax-react'
import { useGetOrders } from './hooks/useOrderData' import { useGetOrders } from './hooks/useOrderData'
import moment from 'moment-jalaali' import moment from 'moment-jalaali'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
@@ -88,13 +88,14 @@ const OrdersList: FC = () => {
{ {
key: 'title', key: 'title',
title: 'عنوان', title: 'عنوان',
render: (item) => { render: (item) => (
return ( <Link
<div> to={Paths.order.details + item.id}
{item.title} className="text-[#0037FF] hover:underline"
</div> >
) {item.title}
} </Link>
),
}, },
{ {
key: 'customer', key: 'customer',
@@ -162,15 +163,10 @@ const OrdersList: FC = () => {
color='black' color='black'
/> />
</Link> </Link>
<Link
to={Paths.order.details + item.id}
>
<Eye size={20} color='#8C90A3' />
</Link>
<Link to={Paths.order.edit + item.id}> <Link to={Paths.order.edit + item.id}>
<Edit2 size={20} color='#0037FF' /> <Edit2 size={20} color='#0037FF' />
</Link> </Link>
<Receipt21 size={20} color='#FF8000' /> {/* <Receipt21 size={20} color='#FF8000' /> */}
</div> </div>
) )
} }