form of converet
This commit is contained in:
@@ -91,4 +91,5 @@ export const Paths = {
|
|||||||
update: '/print/update/',
|
update: '/print/update/',
|
||||||
form: '/print/form/',
|
form: '/print/form/',
|
||||||
},
|
},
|
||||||
|
convertToOrder: '/convert-to-order/',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -132,13 +132,13 @@ const UpdateInvoice: FC = () => {
|
|||||||
const mappedItems: CreatePreInvoiceItemType[] =
|
const mappedItems: CreatePreInvoiceItemType[] =
|
||||||
invoice.items?.length > 0
|
invoice.items?.length > 0
|
||||||
? invoice.items.map((i) => ({
|
? invoice.items.map((i) => ({
|
||||||
id: i.id,
|
id: i.id,
|
||||||
productId: i.product?.id ?? "",
|
productId: i.product?.id ?? "",
|
||||||
quantity: i.quantity ?? 1,
|
quantity: i.quantity ?? 1,
|
||||||
unitPrice: i.unitPrice ?? 0,
|
unitPrice: i.unitPrice ?? 0,
|
||||||
discount: i.discount ?? 0,
|
discount: i.discount ?? 0,
|
||||||
description: i.description ?? "",
|
description: i.description ?? "",
|
||||||
}))
|
}))
|
||||||
: [createEmptyItem()];
|
: [createEmptyItem()];
|
||||||
|
|
||||||
setItems(mappedItems);
|
setItems(mappedItems);
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import Input from "@/components/Input";
|
|||||||
import ProductsSelect from "@/pages/order/components/ProductsSelect";
|
import ProductsSelect from "@/pages/order/components/ProductsSelect";
|
||||||
import type { CreatePreInvoiceItemType } from "../types/Types";
|
import type { CreatePreInvoiceItemType } from "../types/Types";
|
||||||
import { Add, Trash } from "iconsax-react";
|
import { Add, Trash } from "iconsax-react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { Paths } from "@/config/Paths";
|
||||||
type Props = {
|
type Props = {
|
||||||
item: CreatePreInvoiceItemType;
|
item: CreatePreInvoiceItemType;
|
||||||
index: number;
|
index: number;
|
||||||
@@ -10,6 +12,7 @@ type Props = {
|
|||||||
onAdd: () => void;
|
onAdd: () => void;
|
||||||
onRemove: () => void;
|
onRemove: () => void;
|
||||||
canRemove: boolean;
|
canRemove: boolean;
|
||||||
|
onConvertToOrder?: () => void;
|
||||||
error?: Partial<Record<keyof CreatePreInvoiceItemType, string>>;
|
error?: Partial<Record<keyof CreatePreInvoiceItemType, string>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -20,6 +23,7 @@ const InvoiceItemRow: FC<Props> = ({
|
|||||||
onAdd,
|
onAdd,
|
||||||
onRemove,
|
onRemove,
|
||||||
canRemove,
|
canRemove,
|
||||||
|
onConvertToOrder,
|
||||||
error,
|
error,
|
||||||
}) => {
|
}) => {
|
||||||
const total = (item.quantity || 0) * (item.unitPrice || 0) - (item.discount || 0);
|
const total = (item.quantity || 0) * (item.unitPrice || 0) - (item.discount || 0);
|
||||||
@@ -30,6 +34,7 @@ const InvoiceItemRow: FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-wrap items-end gap-4 mt-6">
|
<div className="flex flex-wrap items-end gap-4 mt-6">
|
||||||
|
|
||||||
<div className="min-w-[140px] flex-1">
|
<div className="min-w-[140px] flex-1">
|
||||||
<ProductsSelect
|
<ProductsSelect
|
||||||
value={item.productId}
|
value={item.productId}
|
||||||
@@ -117,6 +122,17 @@ const InvoiceItemRow: FC<Props> = ({
|
|||||||
<Trash size={18} color="#dc2626" />
|
<Trash size={18} color="#dc2626" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
{item.id && (
|
||||||
|
<Link to={Paths.convertToOrder + '/' + item.id}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onConvertToOrder}
|
||||||
|
className="h-10 px-4 rounded-xl border border-primary text-primary text-sm hover:bg-primary/5 transition-colors flex-shrink-0"
|
||||||
|
>
|
||||||
|
تبدیل به سفارش
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,169 @@
|
|||||||
|
import { useState, type FC } from 'react'
|
||||||
|
import { useFormik } from 'formik'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
import Input from '@/components/Input'
|
||||||
|
import Select from '@/components/Select'
|
||||||
|
import UploadBox from '@/components/UploadBox'
|
||||||
|
import Button from '@/components/Button'
|
||||||
|
import { useGetUsers } from '../user/hooks/useUserData'
|
||||||
|
import { useGetProducts } from './hooks/useOrderData'
|
||||||
|
import { useMultiUpload } from '@/pages/uploader/hooks/useUploader'
|
||||||
|
import { OrderStatusEnum } from './enum/OrderEnum'
|
||||||
|
import type { ConvertToOrderItemType } from './types/Types'
|
||||||
|
|
||||||
|
const initialValues: ConvertToOrderItemType = {
|
||||||
|
userId: '',
|
||||||
|
invoiceItemId: '',
|
||||||
|
typeId: '',
|
||||||
|
estimatedDays: 0,
|
||||||
|
productId: '',
|
||||||
|
title: '',
|
||||||
|
attachments: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
const validationSchema = Yup.object({
|
||||||
|
userId: Yup.string().required('انتخاب کاربر اجباری است.'),
|
||||||
|
invoiceItemId: Yup.string(),
|
||||||
|
typeId: Yup.string().required('انتخاب نوع اجباری است.'),
|
||||||
|
estimatedDays: Yup.number().required('تخمین روز اجباری است.').min(0, 'باید عدد مثبت باشد'),
|
||||||
|
productId: Yup.string().required('انتخاب محصول اجباری است.'),
|
||||||
|
title: Yup.string().required('عنوان اجباری است.'),
|
||||||
|
})
|
||||||
|
|
||||||
|
const ConvertToOrders: FC = () => {
|
||||||
|
const { data: users } = useGetUsers()
|
||||||
|
const { data: products } = useGetProducts()
|
||||||
|
const multiUpload = useMultiUpload()
|
||||||
|
const [attachmentFiles, setAttachmentFiles] = useState<File[]>([])
|
||||||
|
|
||||||
|
const formik = useFormik<ConvertToOrderItemType>({
|
||||||
|
initialValues,
|
||||||
|
validationSchema,
|
||||||
|
onSubmit: async (values) => {
|
||||||
|
const attachments: string[] = [...values.attachments]
|
||||||
|
|
||||||
|
if (attachmentFiles.length > 0) {
|
||||||
|
const result = await multiUpload.mutateAsync(attachmentFiles)
|
||||||
|
result?.data?.forEach((item) => {
|
||||||
|
attachments.push(item.url)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload: ConvertToOrderItemType = {
|
||||||
|
...values,
|
||||||
|
estimatedDays: Number(values.estimatedDays),
|
||||||
|
attachments,
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Submit:', payload)
|
||||||
|
// TODO: call API
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-5">
|
||||||
|
<h1 className="text-lg font-light">تبدیل به سفارش</h1>
|
||||||
|
|
||||||
|
<div className="bg-white rounded-3xl p-6 mt-8">
|
||||||
|
<form
|
||||||
|
onSubmit={formik.handleSubmit}
|
||||||
|
className="space-y-6"
|
||||||
|
>
|
||||||
|
<div className="rowTwoInput">
|
||||||
|
<Select
|
||||||
|
items={
|
||||||
|
users?.data?.map((item) => ({
|
||||||
|
label: item.firstName && item.lastName ? `${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: item.id,
|
||||||
|
})) ?? []
|
||||||
|
}
|
||||||
|
label="محصول"
|
||||||
|
placeholder="انتخاب محصول"
|
||||||
|
{...formik.getFieldProps('productId')}
|
||||||
|
error_text={
|
||||||
|
formik.touched.productId && formik.errors.productId ? formik.errors.productId : ''
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rowTwoInput">
|
||||||
|
<Input
|
||||||
|
label="شناسه آیتم فاکتور"
|
||||||
|
{...formik.getFieldProps('invoiceItemId')}
|
||||||
|
error_text={
|
||||||
|
formik.touched.invoiceItemId && formik.errors.invoiceItemId
|
||||||
|
? formik.errors.invoiceItemId
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
items={Object.values(OrderStatusEnum).map((item) => ({
|
||||||
|
label: item,
|
||||||
|
value: item,
|
||||||
|
}))}
|
||||||
|
label="نوع"
|
||||||
|
placeholder="انتخاب نوع"
|
||||||
|
{...formik.getFieldProps('typeId')}
|
||||||
|
error_text={
|
||||||
|
formik.touched.typeId && formik.errors.typeId ? formik.errors.typeId : ''
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rowTwoInput">
|
||||||
|
<Input
|
||||||
|
label="تخمین روز"
|
||||||
|
type="number"
|
||||||
|
{...formik.getFieldProps('estimatedDays')}
|
||||||
|
error_text={
|
||||||
|
formik.touched.estimatedDays && formik.errors.estimatedDays
|
||||||
|
? formik.errors.estimatedDays
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label="عنوان"
|
||||||
|
{...formik.getFieldProps('title')}
|
||||||
|
error_text={
|
||||||
|
formik.touched.title && formik.errors.title ? formik.errors.title : ''
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<UploadBox
|
||||||
|
label="پیوستها"
|
||||||
|
isMultiple
|
||||||
|
onChange={setAttachmentFiles}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button type="submit" isLoading={multiUpload.isPending}>
|
||||||
|
ثبت
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConvertToOrders
|
||||||
@@ -176,3 +176,13 @@ export type CreateOrderType = {
|
|||||||
export interface CreateFinalOrderType extends CreateBaseOrderType {
|
export interface CreateFinalOrderType extends CreateBaseOrderType {
|
||||||
items: CreateOrderType[];
|
items: CreateOrderType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ConvertToOrderItemType = {
|
||||||
|
userId: string;
|
||||||
|
invoiceItemId: string;
|
||||||
|
typeId: string;
|
||||||
|
estimatedDays: number;
|
||||||
|
productId: string;
|
||||||
|
title: string;
|
||||||
|
attachments: string[];
|
||||||
|
};
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import AdminList from "@/pages/admin/List";
|
|||||||
import CreateAdmin from "@/pages/admin/Create";
|
import CreateAdmin from "@/pages/admin/Create";
|
||||||
import EditAdmin from "@/pages/admin/Update";
|
import EditAdmin from "@/pages/admin/Update";
|
||||||
import RequestDetail from "@/pages/requests/Detail";
|
import RequestDetail from "@/pages/requests/Detail";
|
||||||
|
import ConvertToOrders from "@/pages/order/ConvertToOrders";
|
||||||
|
|
||||||
const MainRouter: FC = () => {
|
const MainRouter: FC = () => {
|
||||||
return (
|
return (
|
||||||
@@ -87,6 +88,7 @@ const MainRouter: FC = () => {
|
|||||||
<Route path={Paths.order.details + ":id"} element={<OrderDetail />} />
|
<Route path={Paths.order.details + ":id"} element={<OrderDetail />} />
|
||||||
<Route path={Paths.order.create} element={<NewOrder />} />
|
<Route path={Paths.order.create} element={<NewOrder />} />
|
||||||
<Route path={Paths.order.edit + ":id"} element={<EditOrder />} />
|
<Route path={Paths.order.edit + ":id"} element={<EditOrder />} />
|
||||||
|
<Route path={Paths.convertToOrder + ":id"} element={<ConvertToOrders />} />
|
||||||
|
|
||||||
<Route path={Paths.print.list} element={<SectionList />} />
|
<Route path={Paths.print.list} element={<SectionList />} />
|
||||||
<Route path={Paths.print.create} element={<CreateSection />} />
|
<Route path={Paths.print.create} element={<CreateSection />} />
|
||||||
|
|||||||
Reference in New Issue
Block a user