edit order
This commit is contained in:
+171
-100
@@ -1,58 +1,81 @@
|
||||
import { useEffect, useState, type FC } from 'react'
|
||||
import Button from '@/components/Button'
|
||||
import { TickSquare } from 'iconsax-react'
|
||||
import { useOrderStore } from './store/OrderStore'
|
||||
import { useGetOrderDetails, useUpdateOrder } from './hooks/useOrderData'
|
||||
import { toast } from '@/shared/toast'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { Paths } from '@/config/Paths'
|
||||
import { extractErrorMessage } from '@/config/func'
|
||||
import { useFormik } from 'formik'
|
||||
import type { CreateBaseOrderType } from '../product/types/Types'
|
||||
import { OrderStatusEnum } from './enum/OrderEnum'
|
||||
import * as Yup from 'yup'
|
||||
import Input from '@/components/Input'
|
||||
import Select from '@/components/Select'
|
||||
import Textarea from '@/components/Textarea'
|
||||
import UploadBox from '@/components/UploadBox'
|
||||
import { useGetUsers } from '../user/hooks/useUserData'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import EditOrderComponent from './components/SingleOrderEdit'
|
||||
import type { CreateOrderType } from './types/Types'
|
||||
import { useGetCategory } from '@/pages/product/hooks/useProductData'
|
||||
import { useGetProducts } from './hooks/useOrderData'
|
||||
import type { UpdateOrderType } from './types/Types'
|
||||
import type { OrderDetailDataType } from './types/Types'
|
||||
import { useMultiUpload } from '@/pages/uploader/hooks/useUploader'
|
||||
import { Link } from 'iconsax-react'
|
||||
import TrashWithConfrim from '@/components/TrashWithConfrim'
|
||||
import { getFileNameAndExtensionFromUrl } from '@/config/func'
|
||||
|
||||
const normalizeAttachments = (attachments: unknown[]): string[] => {
|
||||
return attachments.map((att) =>
|
||||
typeof att === 'string' ? att : (att as { url?: string })?.url ?? ''
|
||||
).filter(Boolean)
|
||||
}
|
||||
|
||||
const EditOrder: FC = () => {
|
||||
|
||||
const { id } = useParams()
|
||||
const { data } = useGetOrderDetails(id!)
|
||||
const navigate = useNavigate()
|
||||
const { mutate: submitOrder, isPending } = useUpdateOrder()
|
||||
const { data: users } = useGetUsers()
|
||||
const [showOrders, setShowOrders] = useState([0])
|
||||
const { items, setItems } = useOrderStore()
|
||||
const { data: categories } = useGetCategory()
|
||||
const { data: products } = useGetProducts()
|
||||
const multiUpload = useMultiUpload()
|
||||
const [attachmentFiles, setAttachmentFiles] = useState<File[]>([])
|
||||
|
||||
const formik = useFormik<CreateBaseOrderType>({
|
||||
const order = data?.data as OrderDetailDataType | undefined
|
||||
|
||||
const formik = useFormik<UpdateOrderType>({
|
||||
initialValues: {
|
||||
enableTax: true,
|
||||
estimatedDays: 0,
|
||||
paymentMethod: '',
|
||||
status: OrderStatusEnum.CREATED,
|
||||
userId: '',
|
||||
invoiceItemId: '',
|
||||
typeId: '',
|
||||
estimatedDays: 0,
|
||||
productId: '',
|
||||
title: '',
|
||||
attachments: [],
|
||||
description: '',
|
||||
},
|
||||
validationSchema: Yup.object({
|
||||
userId: Yup.string().required('این فیلد اجباری می باشد.'),
|
||||
estimatedDays: Yup.number().required('این فیلد اجباری می باشد.'),
|
||||
paymentMethod: Yup.string().required('این فیلد اجباری می باشد.'),
|
||||
userId: Yup.string().required('انتخاب کاربر اجباری است.'),
|
||||
typeId: Yup.string().required('انتخاب نوع اجباری است.'),
|
||||
estimatedDays: Yup.number().required('تخمین روز اجباری است.').min(0, 'باید عدد مثبت باشد'),
|
||||
productId: Yup.string().required('انتخاب محصول اجباری است.'),
|
||||
title: Yup.string().required('عنوان اجباری است.'),
|
||||
}),
|
||||
onSubmit: (values) => {
|
||||
values.estimatedDays = Number(values.estimatedDays)
|
||||
onSubmit(values)
|
||||
}
|
||||
})
|
||||
enableReinitialize: true,
|
||||
onSubmit: async (values) => {
|
||||
const attachments: string[] = [...values.attachments]
|
||||
|
||||
const onSubmit = (values: CreateBaseOrderType) => {
|
||||
if (items.length > 0) {
|
||||
const params = {
|
||||
...values,
|
||||
items: items
|
||||
if (attachmentFiles.length > 0) {
|
||||
const result = await multiUpload.mutateAsync(attachmentFiles)
|
||||
result?.data?.forEach((item) => {
|
||||
attachments.push(item.url)
|
||||
})
|
||||
}
|
||||
|
||||
const params: UpdateOrderType = {
|
||||
...values,
|
||||
estimatedDays: Number(values.estimatedDays),
|
||||
attachments,
|
||||
}
|
||||
|
||||
submitOrder({ orderId: id!, params }, {
|
||||
onSuccess: () => {
|
||||
toast('سفارش با موفقیت ویرایش شد', 'success')
|
||||
@@ -62,46 +85,29 @@ const EditOrder: FC = () => {
|
||||
toast(extractErrorMessage(error), 'error')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
toast('حداقل یک سفارش باید اضافه کنید.', 'error')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (data && data?.data) {
|
||||
if (order) {
|
||||
formik.setValues({
|
||||
enableTax: data?.data?.enableTax,
|
||||
estimatedDays: data?.data?.estimatedDays as number,
|
||||
paymentMethod: data?.data?.paymentMethod,
|
||||
status: data?.data?.status as OrderStatusEnum,
|
||||
userId: data?.data?.user.id,
|
||||
userId: order.user?.id ?? '',
|
||||
invoiceItemId: order.invoiceItem ?? '',
|
||||
typeId: order.type?.id ?? '',
|
||||
estimatedDays: order.estimatedDays ?? 0,
|
||||
productId: order.product?.id ?? '',
|
||||
title: order.title ?? '',
|
||||
attachments: normalizeAttachments(order.attachments ?? []),
|
||||
description: (order as OrderDetailDataType & { description?: string })?.description ?? '',
|
||||
})
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data])
|
||||
|
||||
useEffect(() => {
|
||||
if (data && data?.data) {
|
||||
|
||||
const items: CreateOrderType[] = []
|
||||
data?.data?.items.map((item) => {
|
||||
items.push({
|
||||
id: item.id,
|
||||
productId: item.product.id,
|
||||
quantity: item.quantity,
|
||||
attributes: item.attributes,
|
||||
attachments: item.attachments,
|
||||
discount: item.discount,
|
||||
unitPrice: item.unitPrice,
|
||||
description: item.description,
|
||||
})
|
||||
})
|
||||
setItems(items)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data])
|
||||
|
||||
}, [order])
|
||||
|
||||
const handleDeleteAttachment = (url: string) => {
|
||||
const attachments = formik.values.attachments.filter((u) => u !== url)
|
||||
formik.setFieldValue('attachments', attachments)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mt-5'>
|
||||
@@ -112,7 +118,7 @@ const EditOrder: FC = () => {
|
||||
<Button
|
||||
className='w-fit px-5'
|
||||
onClick={() => formik.handleSubmit()}
|
||||
isLoading={isPending}
|
||||
isLoading={isPending || multiUpload.isPending}
|
||||
>
|
||||
<div className='flex gap-2 items-center'>
|
||||
<TickSquare size={20} color='black' />
|
||||
@@ -122,61 +128,126 @@ const EditOrder: FC = () => {
|
||||
</div>
|
||||
|
||||
<div className='bg-white rounded-3xl p-6 mt-8'>
|
||||
<div className='font-light'>اطلاعات اولیه</div>
|
||||
<div className='font-light'>اطلاعات سفارش</div>
|
||||
|
||||
<div className='rowTwoInput mt-6'>
|
||||
<Select
|
||||
items={
|
||||
users?.data?.map((item) => ({
|
||||
label: item.firstName ? `${item.firstName} ${item.lastName}` : item.phone,
|
||||
value: item.id,
|
||||
})) ?? []
|
||||
}
|
||||
label='کاربر'
|
||||
placeholder='انتخاب کاربر'
|
||||
{...formik.getFieldProps('userId')}
|
||||
error_text={
|
||||
formik.touched.userId && formik.errors.userId ? formik.errors.userId : ''
|
||||
}
|
||||
/>
|
||||
|
||||
<Select
|
||||
items={
|
||||
products?.data?.map((item) => ({
|
||||
label: item.title,
|
||||
value: String(item.id),
|
||||
})) ?? []
|
||||
}
|
||||
label='محصول'
|
||||
placeholder='انتخاب محصول'
|
||||
{...formik.getFieldProps('productId')}
|
||||
error_text={
|
||||
formik.touched.productId && formik.errors.productId
|
||||
? formik.errors.productId
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='rowTwoInput mt-6'>
|
||||
<Select
|
||||
items={
|
||||
categories?.data?.map((item) => ({
|
||||
label: item.title,
|
||||
value: String(item.id),
|
||||
})) ?? []
|
||||
}
|
||||
label='نوع'
|
||||
placeholder='انتخاب نوع'
|
||||
{...formik.getFieldProps('typeId')}
|
||||
error_text={
|
||||
formik.touched.typeId && formik.errors.typeId ? formik.errors.typeId : ''
|
||||
}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label='تخمین روز'
|
||||
type='number'
|
||||
{...formik.getFieldProps('estimatedDays')}
|
||||
error_text={formik.errors.estimatedDays && formik.touched.estimatedDays ? formik.errors.estimatedDays : ''}
|
||||
error_text={
|
||||
formik.touched.estimatedDays && formik.errors.estimatedDays
|
||||
? formik.errors.estimatedDays
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<Input
|
||||
label='نوع پرداخت'
|
||||
{...formik.getFieldProps('paymentMethod')}
|
||||
error_text={formik.errors.paymentMethod && formik.touched.paymentMethod ? formik.errors.paymentMethod : ''}
|
||||
label='عنوان'
|
||||
{...formik.getFieldProps('title')}
|
||||
error_text={
|
||||
formik.touched.title && formik.errors.title ? formik.errors.title : ''
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='rowTwoInput mt-6'>
|
||||
|
||||
<Select
|
||||
items={users?.data?.map((item) => {
|
||||
return {
|
||||
label: item.firstName ? item.firstName + ' ' + item.lastName : item.phone,
|
||||
value: item.id
|
||||
}
|
||||
}) || []}
|
||||
label='انتخاب کاربر'
|
||||
placeholder='انتخاب'
|
||||
{...formik.getFieldProps('userId')}
|
||||
error_text={formik.errors.userId && formik.touched.userId ? formik.errors.userId : ''}
|
||||
/>
|
||||
|
||||
<Select
|
||||
label='وضعیت سفارش'
|
||||
placeholder='انتخاب'
|
||||
items={Object.values(OrderStatusEnum).map((item) => {
|
||||
return {
|
||||
label: item,
|
||||
value: item
|
||||
}
|
||||
})}
|
||||
{...formik.getFieldProps('status')}
|
||||
error_text={formik.errors.status && formik.touched.status ? formik.errors.status : ''}
|
||||
<div className='mt-6'>
|
||||
<Textarea
|
||||
label='توضیحات'
|
||||
placeholder='توضیحات را وارد کنید...'
|
||||
{...formik.getFieldProps('description')}
|
||||
error_text={
|
||||
formik.touched.description && formik.errors.description
|
||||
? formik.errors.description
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<UploadBox
|
||||
label='پیوستها'
|
||||
isMultiple
|
||||
onChange={setAttachmentFiles}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{formik.values.attachments?.length ? (
|
||||
<div className='mt-5'>
|
||||
<div className='text-sm mb-2'>پیوستهای فعلی</div>
|
||||
<div className='flex flex-wrap gap-4'>
|
||||
{formik.values.attachments.map((url) => (
|
||||
<div
|
||||
key={url}
|
||||
className='flex mt-2 px-2 py-2 rounded-lg items-center gap-2 text-xs text-[#0037FF] border border-[#0037FF]'
|
||||
>
|
||||
<Link size={16} color='#0037FF' />
|
||||
<div>{getFileNameAndExtensionFromUrl(url).fileName}</div>
|
||||
<div className='w-9 flex justify-end'>
|
||||
<TrashWithConfrim
|
||||
colorIcon='red'
|
||||
onDelete={() => handleDeleteAttachment(url)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{items.map((item) => {
|
||||
return (
|
||||
<EditOrderComponent addNewItem={() => setShowOrders([...showOrders, showOrders.length])} key={item.productId} initialValues={item} />
|
||||
)
|
||||
})}
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default EditOrder
|
||||
export default EditOrder
|
||||
|
||||
@@ -2,8 +2,8 @@ import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import * as api from "../service/OrderService";
|
||||
import type {
|
||||
AddTicketType,
|
||||
CreateFinalOrderType,
|
||||
OrderPrintFormType,
|
||||
UpdateOrderType,
|
||||
} from "../types/Types";
|
||||
|
||||
export const useGetOrders = (params?: api.GetOrdersParams) => {
|
||||
@@ -74,7 +74,7 @@ export const useUpdateOrder = () => {
|
||||
params,
|
||||
}: {
|
||||
orderId: string;
|
||||
params: CreateFinalOrderType;
|
||||
params: UpdateOrderType;
|
||||
}) => api.updateOrder(orderId, params),
|
||||
});
|
||||
};
|
||||
@@ -86,7 +86,7 @@ export const useGetProducts = () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetAttributes = (id?: number) => {
|
||||
export const useGetAttributes = (id?: string) => {
|
||||
return useQuery({
|
||||
queryKey: ["attributes", id],
|
||||
queryFn: () => api.getAttributes(id),
|
||||
|
||||
@@ -9,6 +9,7 @@ import type {
|
||||
OrderPrintFormType,
|
||||
OrderPrintResponseType,
|
||||
TicketsResponseType,
|
||||
UpdateOrderType,
|
||||
} from "../types/Types";
|
||||
import type {
|
||||
FieldResponseType,
|
||||
@@ -28,7 +29,7 @@ const cleanParams = (params?: GetOrdersParams) => {
|
||||
if (!params) return undefined;
|
||||
return Object.fromEntries(
|
||||
Object.entries(params).filter(
|
||||
(entry) => entry[1] != null && entry[1] !== '',
|
||||
(entry) => entry[1] != null && entry[1] !== "",
|
||||
) as [string, string][],
|
||||
);
|
||||
};
|
||||
@@ -81,7 +82,7 @@ export const submitOrder = async (params: CreateFinalOrderType) => {
|
||||
|
||||
export const updateOrder = async (
|
||||
orderId: string,
|
||||
params: CreateFinalOrderType,
|
||||
params: UpdateOrderType,
|
||||
) => {
|
||||
const { data } = await axios.patch(`/admin/orders/${orderId}`, params);
|
||||
return data;
|
||||
@@ -92,7 +93,7 @@ export const getProducts = async () => {
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getAttributes = async (productId?: number) => {
|
||||
export const getAttributes = async (productId?: string) => {
|
||||
const { data } = await axios.get<FieldResponseType>(
|
||||
`/admin/entity/${productId}/field`,
|
||||
);
|
||||
|
||||
@@ -238,6 +238,17 @@ export interface CreateFinalOrderType extends CreateBaseOrderType {
|
||||
items: CreateOrderType[];
|
||||
}
|
||||
|
||||
export type UpdateOrderType = {
|
||||
userId: string;
|
||||
invoiceItemId: string;
|
||||
typeId: string;
|
||||
estimatedDays: number;
|
||||
productId: string;
|
||||
title: string;
|
||||
attachments: string[];
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type ConvertToOrderItemType = {
|
||||
invoiceItemId: string;
|
||||
productId?: string;
|
||||
|
||||
@@ -15,7 +15,7 @@ const ProductList: FC = () => {
|
||||
const { data: products, refetch } = useGetProducts()
|
||||
const { mutate: deleteProduct, isPending: isDeleting } = useDeleteProduct()
|
||||
|
||||
const handleDelete = (id: number) => {
|
||||
const handleDelete = (id: string) => {
|
||||
deleteProduct(id, {
|
||||
onSuccess: () => {
|
||||
refetch()
|
||||
|
||||
@@ -88,7 +88,7 @@ export const useUpdateProduct = () => {
|
||||
|
||||
export const useDeleteProduct = () => {
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => api.deleteProduct(id),
|
||||
mutationFn: (id: string) => api.deleteProduct(id),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ export const updateProduct = async (id: string, params:CreateProductType) => {
|
||||
return data;
|
||||
};
|
||||
|
||||
export const deleteProduct = async (id: number) => {
|
||||
export const deleteProduct = async (id: string) => {
|
||||
const { data } = await axios.delete(`/admin/products/${id}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -122,7 +122,7 @@ const CreateUser: FC = () => {
|
||||
|
||||
<div className="mt-6">
|
||||
<Input
|
||||
label="حداکثر اعتبار (ریال)"
|
||||
label="حداکثر اعتبار (تومان)"
|
||||
type="number"
|
||||
min={0}
|
||||
placeholder="۰"
|
||||
|
||||
@@ -27,8 +27,7 @@ const initialValues: CreateUserType = {
|
||||
|
||||
const validationSchema = Yup.object({
|
||||
phone: Yup.string()
|
||||
.required("شماره موبایل اجباری است")
|
||||
.matches(/^09[0-9]{9}$/, "شماره موبایل معتبر نیست"),
|
||||
.required("شماره موبایل اجباری است"),
|
||||
firstName: Yup.string().required("نام اجباری است"),
|
||||
lastName: Yup.string().required("نام خانوادگی اجباری است"),
|
||||
address: Yup.string(),
|
||||
@@ -169,7 +168,7 @@ const UpdateUser: FC = () => {
|
||||
|
||||
<div className="mt-6">
|
||||
<Input
|
||||
label="حداکثر اعتبار (ریال)"
|
||||
label="حداکثر اعتبار (تومان)"
|
||||
type="number"
|
||||
min={0}
|
||||
placeholder="۰"
|
||||
|
||||
Reference in New Issue
Block a user