This commit is contained in:
+216
-102
@@ -1,13 +1,23 @@
|
||||
import { useState, type FC } from 'react'
|
||||
import { useState, type FC, type ReactNode } from 'react'
|
||||
import { useFormik } from 'formik'
|
||||
import * as Yup from 'yup'
|
||||
import moment from 'moment-jalaali'
|
||||
import { Link, useNavigate, useSearchParams } from 'react-router-dom'
|
||||
import {
|
||||
Box1,
|
||||
Calendar,
|
||||
DocumentText,
|
||||
Profile2User,
|
||||
ReceiptText,
|
||||
TickSquare,
|
||||
WalletMoney,
|
||||
} from 'iconsax-react'
|
||||
import Input from '@/components/Input'
|
||||
import Textarea from '@/components/Textarea'
|
||||
import Select from '@/components/Select'
|
||||
import UploadBox from '@/components/UploadBox'
|
||||
import Button from '@/components/Button'
|
||||
import BackButton from '@/components/BackButton'
|
||||
import { useMultiUpload } from '@/pages/uploader/hooks/useUploader'
|
||||
import type { ConvertToOrderItemType } from './types/Types'
|
||||
import { useConverToOrder, useGetProducts } from './hooks/useOrderData'
|
||||
@@ -24,6 +34,35 @@ import { Checkbox } from '@/components/ui/checkbox'
|
||||
const formatAmount = (amount: number) =>
|
||||
Number(amount).toLocaleString('fa-IR') + ' ریال'
|
||||
|
||||
type InfoFieldProps = {
|
||||
label: string
|
||||
value: ReactNode
|
||||
icon?: ReactNode
|
||||
}
|
||||
|
||||
const InfoField: FC<InfoFieldProps> = ({ label, value, icon }) => (
|
||||
<div className="rounded-2xl border border-border/70 bg-[#FBFCFF] p-4">
|
||||
<div className="mb-2 flex items-center gap-2 text-[12px] text-[#7D8597]">
|
||||
{icon}
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
<div className="text-[15px] font-medium text-[#1F2937]">{value}</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
type SummaryRowProps = {
|
||||
label: string
|
||||
value: string
|
||||
valueClassName?: string
|
||||
}
|
||||
|
||||
const SummaryRow: FC<SummaryRowProps> = ({ label, value, valueClassName }) => (
|
||||
<div className="flex items-center justify-between rounded-xl border border-border/70 bg-white px-4 py-3">
|
||||
<span className="text-[13px] text-[#6B7280]">{label}</span>
|
||||
<span className={`text-[14px] font-semibold ${valueClassName ?? ''}`}>{value}</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
const getInitialValues = (invoiceItemId: string | null): ConvertToOrderItemType => ({
|
||||
invoiceItemId: invoiceItemId ?? '',
|
||||
productId: '',
|
||||
@@ -46,11 +85,10 @@ const getValidationSchema = (invoiceItemId: string | null) =>
|
||||
estimatedDays: Yup.number().required('تخمین روز اجباری است.').min(0, 'باید عدد مثبت باشد'),
|
||||
title: Yup.string().required('عنوان اجباری است.'),
|
||||
description: Yup.string(),
|
||||
designerId: Yup.string().required('این فیلد اجباری است')
|
||||
designerId: Yup.string().required('این فیلد اجباری است'),
|
||||
})
|
||||
|
||||
const ConvertToOrders: FC = () => {
|
||||
|
||||
const navigate = useNavigate()
|
||||
const [searchParams] = useSearchParams()
|
||||
const invoiceItemId = searchParams.get('invoiceItemId')
|
||||
@@ -99,7 +137,7 @@ const ConvertToOrders: FC = () => {
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(extractErrorMessage(error))
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -110,94 +148,54 @@ const ConvertToOrders: FC = () => {
|
||||
: invoiceItem?.invoice?.user?.phone ?? '-'
|
||||
|
||||
const discounts = invoiceItem ? formatItemDiscountDisplay(invoiceItem) : null
|
||||
const isSubmitting = multiUpload.isPending || isPending
|
||||
const backTo = invoiceItem?.invoice?.id
|
||||
? Paths.perfomaInvoice.detail + invoiceItem.invoice.id
|
||||
: Paths.order.list
|
||||
|
||||
return (
|
||||
<div className="mt-5">
|
||||
<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 className="mt-5 space-y-8">
|
||||
<div className="flex flex-wrap items-center justify-between gap-4">
|
||||
<div className="space-y-3">
|
||||
<BackButton to={backTo} />
|
||||
<div className="flex items-center gap-2 text-[#111827]">
|
||||
<DocumentText size={20} color="#111827" />
|
||||
<h1 className="text-xl font-medium">تبدیل به سفارش</h1>
|
||||
</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"
|
||||
{invoiceItemId && invoiceItem?.product?.title && (
|
||||
<p className="text-sm text-[#6B7280]">
|
||||
ایجاد سفارش از آیتم «{invoiceItem.product.title}»
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
className="w-fit px-5"
|
||||
onClick={() => formik.handleSubmit()}
|
||||
isLoading={isSubmitting}
|
||||
>
|
||||
#{invoiceItem.invoice.invoiceNumber}
|
||||
</Link>
|
||||
<div className="flex items-center gap-2">
|
||||
<TickSquare size={20} color="black" />
|
||||
<span>ثبت سفارش</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className="mb-1.5 text-[13px] text-[#888888]">مشتری</div>
|
||||
<div className="text-[15px]">{customerName}</div>
|
||||
</Button>
|
||||
</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">
|
||||
<form
|
||||
onSubmit={formik.handleSubmit}
|
||||
className="space-y-6"
|
||||
<div
|
||||
className={
|
||||
invoiceItemId
|
||||
? '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="font-medium text-[#111827]">اطلاعات سفارش</div>
|
||||
<p className="mt-1 text-[13px] text-[#6B7280]">
|
||||
جزئیات مورد نیاز برای ثبت سفارش را تکمیل کنید.
|
||||
</p>
|
||||
|
||||
<form onSubmit={formik.handleSubmit} className="mt-6 space-y-6">
|
||||
{!invoiceItemId && (
|
||||
<div className="rowTwoInput">
|
||||
<div className="w-full">
|
||||
@@ -211,7 +209,7 @@ const ConvertToOrders: FC = () => {
|
||||
}}
|
||||
/>
|
||||
{formik.touched.userId && formik.errors.userId ? (
|
||||
<div className="text-xs text-right text-red-600 mt-2 mr-2 font-medium">
|
||||
<div className="mt-2 mr-2 text-right text-xs font-medium text-red-600">
|
||||
{formik.errors.userId}
|
||||
</div>
|
||||
) : null}
|
||||
@@ -236,19 +234,20 @@ const ConvertToOrders: FC = () => {
|
||||
)}
|
||||
|
||||
<div className="rowTwoInput">
|
||||
|
||||
<Select
|
||||
items={admins?.data?.map((item) => {
|
||||
return {
|
||||
items={
|
||||
admins?.data?.map((item) => ({
|
||||
label: item.firstName + ' ' + item.lastName,
|
||||
value: item.id
|
||||
value: item.id,
|
||||
})) || []
|
||||
}
|
||||
}) || []}
|
||||
label="طراح"
|
||||
placeholder="انتخاب طراح"
|
||||
{...formik.getFieldProps('designerId')}
|
||||
error_text={
|
||||
formik.touched.designerId && formik.errors.designerId ? formik.errors.designerId : ''
|
||||
formik.touched.designerId && formik.errors.designerId
|
||||
? formik.errors.designerId
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -263,7 +262,9 @@ const ConvertToOrders: FC = () => {
|
||||
placeholder="انتخاب نوع"
|
||||
{...formik.getFieldProps('typeId')}
|
||||
error_text={
|
||||
formik.touched.typeId && formik.errors.typeId ? formik.errors.typeId : ''
|
||||
formik.touched.typeId && formik.errors.typeId
|
||||
? formik.errors.typeId
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
@@ -284,12 +285,13 @@ const ConvertToOrders: FC = () => {
|
||||
label="عنوان"
|
||||
{...formik.getFieldProps('title')}
|
||||
error_text={
|
||||
formik.touched.title && formik.errors.title ? formik.errors.title : ''
|
||||
formik.touched.title && formik.errors.title
|
||||
? formik.errors.title
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Textarea
|
||||
label="توضیحات"
|
||||
placeholder="توضیحات را وارد کنید..."
|
||||
@@ -300,14 +302,19 @@ const ConvertToOrders: FC = () => {
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{invoiceItemId && (
|
||||
<div>
|
||||
<div
|
||||
className={`rounded-2xl border border-border/70 bg-[#FBFCFF] p-4 ${
|
||||
!hasLinkedRequest && !isInvoiceItemLoading
|
||||
? 'opacity-60'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`flex items-center gap-2 ${
|
||||
!hasLinkedRequest && !isInvoiceItemLoading
|
||||
? 'pointer-events-none opacity-50'
|
||||
? 'pointer-events-none'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
@@ -320,7 +327,7 @@ const ConvertToOrders: FC = () => {
|
||||
/>
|
||||
<label
|
||||
htmlFor="keepRequestChats"
|
||||
className="cursor-pointer select-none text-[13px]"
|
||||
className="cursor-pointer select-none text-[13px] text-[#1F2937]"
|
||||
>
|
||||
انتقال چتهای درخواست به سفارش
|
||||
</label>
|
||||
@@ -333,7 +340,7 @@ const ConvertToOrders: FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<div className="rounded-2xl border border-dashed border-border/70 bg-[#FBFCFF] p-4">
|
||||
<UploadBox
|
||||
label="پیوستها"
|
||||
isMultiple
|
||||
@@ -341,13 +348,120 @@ const ConvertToOrders: FC = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit" isLoading={multiUpload.isPending || isPending}>
|
||||
ثبت
|
||||
<div className="flex justify-end xl:hidden">
|
||||
<Button type="submit" className="w-fit px-5" isLoading={isSubmitting}>
|
||||
<div className="flex items-center gap-2">
|
||||
<TickSquare size={20} color="black" />
|
||||
<span>ثبت سفارش</span>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{invoiceItemId && (
|
||||
<aside className="h-fit space-y-4">
|
||||
<div className="rounded-3xl border border-border/70 bg-[#F7F9FF] p-5 shadow-sm">
|
||||
<div className="mb-4 flex items-center gap-2 text-[#111827]">
|
||||
<ReceiptText size={18} color="#111827" />
|
||||
<span className="font-medium">آیتم پیش فاکتور</span>
|
||||
</div>
|
||||
|
||||
{isInvoiceItemLoading ? (
|
||||
<div className="flex min-h-[140px] items-center justify-center text-[#888888]">
|
||||
در حال بارگذاری...
|
||||
</div>
|
||||
) : invoiceItem ? (
|
||||
<div className="space-y-3">
|
||||
<InfoField
|
||||
label="پیش فاکتور"
|
||||
icon={<ReceiptText size={14} color="#7D8597" />}
|
||||
value={
|
||||
<Link
|
||||
to={Paths.perfomaInvoice.detail + invoiceItem.invoice.id}
|
||||
className="text-primary hover:underline"
|
||||
>
|
||||
#{invoiceItem.invoice.invoiceNumber}
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
<InfoField
|
||||
label="مشتری"
|
||||
value={customerName}
|
||||
icon={<Profile2User size={14} color="#7D8597" />}
|
||||
/>
|
||||
<InfoField
|
||||
label="محصول"
|
||||
value={invoiceItem.product?.title ?? '-'}
|
||||
icon={<Box1 size={14} color="#7D8597" />}
|
||||
/>
|
||||
<InfoField
|
||||
label="تاریخ تایید"
|
||||
icon={<Calendar size={14} color="#7D8597" />}
|
||||
value={
|
||||
invoiceItem.confirmedAt
|
||||
? moment(invoiceItem.confirmedAt).format(
|
||||
'jYYYY/jMM/jDD HH:mm',
|
||||
)
|
||||
: '-'
|
||||
}
|
||||
/>
|
||||
{invoiceItem.description && (
|
||||
<div className="rounded-2xl border border-border/70 bg-white p-4">
|
||||
<div className="mb-2 text-[12px] text-[#7D8597]">
|
||||
توضیحات
|
||||
</div>
|
||||
<div className="whitespace-pre-wrap text-[14px] text-[#1F2937]">
|
||||
{invoiceItem.description}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-xl border border-red-100 bg-red-50 px-3 py-3 text-[13px] text-red-700">
|
||||
آیتم پیش فاکتور یافت نشد.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{invoiceItem && !isInvoiceItemLoading && (
|
||||
<div className="rounded-3xl border border-border/70 bg-[#F7F9FF] p-5 shadow-sm">
|
||||
<div className="mb-4 flex items-center gap-2 text-[#111827]">
|
||||
<WalletMoney size={18} color="#111827" />
|
||||
<span className="font-medium">خلاصه مالی</span>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<SummaryRow
|
||||
label="تعداد"
|
||||
value={String(invoiceItem.quantity ?? '-')}
|
||||
/>
|
||||
<SummaryRow
|
||||
label="مبلغ واحد"
|
||||
value={
|
||||
invoiceItem.unitPrice != null
|
||||
? formatAmount(invoiceItem.unitPrice)
|
||||
: '-'
|
||||
}
|
||||
/>
|
||||
<SummaryRow
|
||||
label="تخفیف (مبلغ)"
|
||||
value={discounts?.value ?? '-'}
|
||||
/>
|
||||
<SummaryRow
|
||||
label="تخفیف (درصد)"
|
||||
value={discounts?.percent ?? '-'}
|
||||
/>
|
||||
<SummaryRow
|
||||
label="مبلغ کل"
|
||||
value={formatAmount(invoiceItem.total ?? 0)}
|
||||
valueClassName="text-[#111827]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</aside>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user