form of converet

This commit is contained in:
hamid zarghami
2026-02-21 16:42:23 +03:30
parent f2ebdc9836
commit 502e8c538f
6 changed files with 205 additions and 7 deletions
+1
View File
@@ -91,4 +91,5 @@ export const Paths = {
update: '/print/update/', update: '/print/update/',
form: '/print/form/', form: '/print/form/',
}, },
convertToOrder: '/convert-to-order/',
}; };
+7 -7
View File
@@ -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>
); );
+169
View File
@@ -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
+10
View File
@@ -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[];
};
+2
View File
@@ -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 />} />