clean code
This commit is contained in:
@@ -1,152 +1,17 @@
|
|||||||
import Button from "@/components/Button";
|
import DesignerRequestForm from "@/pages/designer/components/DesignerRequestForm";
|
||||||
import DatePicker from "@/components/DatePicker";
|
import { useSubmitDesignRequest } from "@/pages/designer/hooks/useDesignerData";
|
||||||
import Error from "@/components/Error";
|
|
||||||
import Input from "@/components/Input";
|
|
||||||
import Textarea from "@/components/Textarea";
|
|
||||||
import { toast } from "@/components/Toast";
|
|
||||||
import UploadBoxDraggble from "@/components/UploadBoxDraggble";
|
|
||||||
import { extractErrorMessage } from "@/helpers/utils";
|
|
||||||
import { usePageTitle } from "@/hooks/usePageTitle";
|
import { usePageTitle } from "@/hooks/usePageTitle";
|
||||||
import { useInitiateDesignRequest } from "@/pages/designer/hooks/useDesignerData";
|
|
||||||
import type { DesignerRequestFormValues } from "@/pages/designer/types/Types";
|
|
||||||
import { useMultipleUpload } from "@/pages/uploader/hooks/useUploaderData";
|
|
||||||
import { Form, Formik, type FormikHelpers, type FormikProps } from "formik";
|
|
||||||
import { type FC } from "react";
|
import { type FC } from "react";
|
||||||
import * as Yup from "yup";
|
|
||||||
|
|
||||||
const PRICE_PER_PAGE = 1000;
|
|
||||||
|
|
||||||
const initialValues: DesignerRequestFormValues = {
|
|
||||||
title: "",
|
|
||||||
count: "",
|
|
||||||
desc: "",
|
|
||||||
expectedDate: "",
|
|
||||||
attachments: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const validationSchema = Yup.object({
|
|
||||||
title: Yup.string().trim().required("عنوان کاتالوگ الزامی است"),
|
|
||||||
count: Yup.number()
|
|
||||||
.transform((value, originalValue) => (originalValue === "" ? undefined : value))
|
|
||||||
.optional()
|
|
||||||
.min(1, "حداقل تعداد صفحات ۱ است"),
|
|
||||||
desc: Yup.string().trim(),
|
|
||||||
expectedDate: Yup.string().required("زمان تحویل الزامی است"),
|
|
||||||
attachments: Yup.array(),
|
|
||||||
});
|
|
||||||
|
|
||||||
type DesignerRequestFormFieldsProps = FormikProps<DesignerRequestFormValues>;
|
|
||||||
|
|
||||||
const DesignerRequestFormFields: FC<DesignerRequestFormFieldsProps> = ({ values, errors, touched, isSubmitting, setFieldValue, setFieldTouched, handleChange, handleBlur }) => {
|
|
||||||
const totalPrice = (values.count === "" ? 0 : values.count) * PRICE_PER_PAGE;
|
|
||||||
|
|
||||||
const handleAttachmentChange = (files: File[]) => {
|
|
||||||
setFieldValue("attachments", files);
|
|
||||||
setFieldTouched("attachments", true);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form className="mt-6 space-y-5">
|
|
||||||
<Input label="عنوان کاتالوگ" name="title" value={values.title} onChange={handleChange} onBlur={handleBlur} error_text={touched.title ? errors.title : undefined} />
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
|
||||||
<DatePicker
|
|
||||||
label="زمان تحویل موردنظر"
|
|
||||||
placeholder="تاریخ را انتخاب کنید"
|
|
||||||
defaulValue={values.expectedDate}
|
|
||||||
onChange={(date) => {
|
|
||||||
setFieldValue("expectedDate", date);
|
|
||||||
setFieldTouched("expectedDate", true);
|
|
||||||
}}
|
|
||||||
error_text={touched.expectedDate ? errors.expectedDate : undefined}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
label="تعداد صفحات"
|
|
||||||
name="count"
|
|
||||||
type="number"
|
|
||||||
isNotRequired
|
|
||||||
value={values.count}
|
|
||||||
onChange={(event) => {
|
|
||||||
const value = event.target.value;
|
|
||||||
setFieldValue("count", value === "" ? "" : Number(value));
|
|
||||||
}}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
error_text={touched.count ? errors.count : undefined}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Textarea label="توضیحات" name="desc" value={values.desc} onChange={handleChange} onBlur={handleBlur} className="min-h-[96px]" error_text={touched.desc ? errors.desc : undefined} />
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div className="mb-2 text-sm">فایل های ضمیمه</div>
|
|
||||||
<UploadBoxDraggble label="فایل مورد نظر را آپلود کنید" onChange={handleAttachmentChange} isMultiple isFile isLoading={isSubmitting} loadingLabel="در حال ثبت درخواست..." />
|
|
||||||
{touched.attachments && errors.attachments ? <Error errorText={String(errors.attachments)} /> : null}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex">
|
|
||||||
<div className="flex-1"></div>
|
|
||||||
<div className="flex-1 flex gap-4">
|
|
||||||
<div className="w-[250px]">
|
|
||||||
<Input label="قیمت به ازای هر صفحه" value={`${PRICE_PER_PAGE.toLocaleString("fa-IR")} تومان`} readOnly />
|
|
||||||
</div>
|
|
||||||
<Input label="مبلغ قابل پرداخت برای شما" value={`${totalPrice.toLocaleString("fa-IR")} تومان`} readOnly />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end mt-5">
|
|
||||||
<Button type="submit" className="w-fit px-6" disabled={isSubmitting}>
|
|
||||||
ثبت درخواست
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const DesignerRequest: FC = () => {
|
const DesignerRequest: FC = () => {
|
||||||
usePageTitle("درخواست طراحی");
|
usePageTitle("درخواست طراحی");
|
||||||
const { mutateAsync: uploadFiles } = useMultipleUpload();
|
const handleSubmit = useSubmitDesignRequest();
|
||||||
const { mutateAsync: initiateDesignRequest } = useInitiateDesignRequest();
|
|
||||||
|
|
||||||
const handleSubmit = async (values: DesignerRequestFormValues, { setSubmitting, resetForm }: FormikHelpers<DesignerRequestFormValues>) => {
|
|
||||||
let attachmentUrls: string[] = [];
|
|
||||||
|
|
||||||
if (values.attachments.length > 0) {
|
|
||||||
const uploadResponse = await uploadFiles(values.attachments);
|
|
||||||
attachmentUrls = uploadResponse.data.map((item) => item.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { attachments, count, ...rest } = values;
|
|
||||||
|
|
||||||
await initiateDesignRequest(
|
|
||||||
{
|
|
||||||
...rest,
|
|
||||||
count: count === "" ? undefined : count,
|
|
||||||
attachments: attachmentUrls,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onSuccess: (data) => {
|
|
||||||
toast("صورت حساب با موفقیت ثبت شد", "success");
|
|
||||||
window.open(`${import.meta.env.VITE_INVOICE_URL}${data?.data?.invoiceId}`, "_blank");
|
|
||||||
setSubmitting(false);
|
|
||||||
resetForm();
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
toast(extractErrorMessage(error), "error");
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
toast("درخواست با موفقیت ثبت شد", "success");
|
|
||||||
resetForm();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-4 w-full">
|
<div className="mt-4 w-full">
|
||||||
<h1 className="">درخواست طراحی کاتالوگ</h1>
|
<h1>درخواست طراحی کاتالوگ</h1>
|
||||||
<div className="rounded-[32px] bg-white p-4 md:p-6 lg:p-8 mt-6">
|
<div className="mt-6 rounded-[32px] bg-white p-4 md:p-6 lg:p-8">
|
||||||
<Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={handleSubmit}>
|
<DesignerRequestForm onSubmit={handleSubmit} />
|
||||||
{(formikProps) => <DesignerRequestFormFields {...formikProps} />}
|
|
||||||
</Formik>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import Button from "@/components/Button";
|
||||||
|
import DatePicker from "@/components/DatePicker";
|
||||||
|
import Error from "@/components/Error";
|
||||||
|
import Input from "@/components/Input";
|
||||||
|
import Textarea from "@/components/Textarea";
|
||||||
|
import UploadBoxDraggble from "@/components/UploadBoxDraggble";
|
||||||
|
import {
|
||||||
|
designerRequestInitialValues,
|
||||||
|
designerRequestValidationSchema,
|
||||||
|
getTotalPrice,
|
||||||
|
PRICE_PER_PAGE,
|
||||||
|
} from "@/pages/designer/designerRequestForm";
|
||||||
|
import type { DesignerRequestFormValues } from "@/pages/designer/types/Types";
|
||||||
|
import { Form, Formik, useFormikContext, type FormikHelpers } from "formik";
|
||||||
|
import { type FC } from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onSubmit: (
|
||||||
|
values: DesignerRequestFormValues,
|
||||||
|
helpers: FormikHelpers<DesignerRequestFormValues>,
|
||||||
|
) => void | Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DesignerRequestFormFields: FC = () => {
|
||||||
|
const {
|
||||||
|
values,
|
||||||
|
errors,
|
||||||
|
touched,
|
||||||
|
isSubmitting,
|
||||||
|
setFieldValue,
|
||||||
|
setFieldTouched,
|
||||||
|
handleChange,
|
||||||
|
handleBlur,
|
||||||
|
} = useFormikContext<DesignerRequestFormValues>();
|
||||||
|
|
||||||
|
const totalPrice = getTotalPrice(values.count);
|
||||||
|
|
||||||
|
const handleAttachmentChange = (files: File[]) => {
|
||||||
|
setFieldValue("attachments", files);
|
||||||
|
setFieldTouched("attachments", true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form className="mt-6 space-y-5">
|
||||||
|
<Input
|
||||||
|
label="عنوان کاتالوگ"
|
||||||
|
name="title"
|
||||||
|
value={values.title}
|
||||||
|
onChange={handleChange}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
error_text={touched.title ? errors.title : undefined}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||||
|
<DatePicker
|
||||||
|
label="زمان تحویل موردنظر"
|
||||||
|
placeholder="تاریخ را انتخاب کنید"
|
||||||
|
defaulValue={values.expectedDate}
|
||||||
|
onChange={(date) => {
|
||||||
|
setFieldValue("expectedDate", date);
|
||||||
|
setFieldTouched("expectedDate", true);
|
||||||
|
}}
|
||||||
|
error_text={touched.expectedDate ? errors.expectedDate : undefined}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
label="تعداد صفحات"
|
||||||
|
name="count"
|
||||||
|
type="number"
|
||||||
|
isNotRequired
|
||||||
|
value={values.count}
|
||||||
|
onChange={(event) => {
|
||||||
|
const value = event.target.value;
|
||||||
|
setFieldValue("count", value === "" ? "" : Number(value));
|
||||||
|
}}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
error_text={touched.count ? errors.count : undefined}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Textarea
|
||||||
|
label="توضیحات"
|
||||||
|
name="desc"
|
||||||
|
value={values.desc}
|
||||||
|
onChange={handleChange}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
className="min-h-[96px]"
|
||||||
|
error_text={touched.desc ? errors.desc : undefined}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className="mb-2 text-sm">فایل های ضمیمه</div>
|
||||||
|
<UploadBoxDraggble
|
||||||
|
label="فایل مورد نظر را آپلود کنید"
|
||||||
|
onChange={handleAttachmentChange}
|
||||||
|
isMultiple
|
||||||
|
isFile
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
loadingLabel="در حال ثبت درخواست..."
|
||||||
|
/>
|
||||||
|
{touched.attachments && errors.attachments ? (
|
||||||
|
<Error errorText={String(errors.attachments)} />
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex">
|
||||||
|
<div className="flex-1" />
|
||||||
|
<div className="flex flex-1 gap-4">
|
||||||
|
<div className="w-[250px]">
|
||||||
|
<Input
|
||||||
|
label="قیمت به ازای هر صفحه"
|
||||||
|
value={`${PRICE_PER_PAGE.toLocaleString("fa-IR")} تومان`}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
label="مبلغ قابل پرداخت برای شما"
|
||||||
|
value={`${totalPrice.toLocaleString("fa-IR")} تومان`}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 flex justify-end">
|
||||||
|
<Button type="submit" className="w-fit px-6" disabled={isSubmitting}>
|
||||||
|
ثبت درخواست
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const DesignerRequestForm: FC<Props> = ({ onSubmit }) => (
|
||||||
|
<Formik
|
||||||
|
initialValues={designerRequestInitialValues}
|
||||||
|
validationSchema={designerRequestValidationSchema}
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
>
|
||||||
|
<DesignerRequestFormFields />
|
||||||
|
</Formik>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default DesignerRequestForm;
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import type {
|
||||||
|
DesignRequestInitiateParams,
|
||||||
|
DesignerRequestFormValues,
|
||||||
|
} from "@/pages/designer/types/Types";
|
||||||
|
import * as Yup from "yup";
|
||||||
|
|
||||||
|
export const PRICE_PER_PAGE = 1000;
|
||||||
|
|
||||||
|
export const designerRequestInitialValues: DesignerRequestFormValues = {
|
||||||
|
title: "",
|
||||||
|
count: "",
|
||||||
|
desc: "",
|
||||||
|
expectedDate: "",
|
||||||
|
attachments: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const designerRequestValidationSchema = Yup.object({
|
||||||
|
title: Yup.string().trim().required("عنوان کاتالوگ الزامی است"),
|
||||||
|
count: Yup.number()
|
||||||
|
.transform((value, originalValue) =>
|
||||||
|
originalValue === "" ? undefined : value,
|
||||||
|
)
|
||||||
|
.optional()
|
||||||
|
.min(1, "حداقل تعداد صفحات ۱ است"),
|
||||||
|
desc: Yup.string().trim(),
|
||||||
|
expectedDate: Yup.string().required("زمان تحویل الزامی است"),
|
||||||
|
attachments: Yup.array(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getTotalPrice = (count: DesignerRequestFormValues["count"]) =>
|
||||||
|
(count === "" ? 0 : count) * PRICE_PER_PAGE;
|
||||||
|
|
||||||
|
export const toDesignRequestParams = (
|
||||||
|
values: DesignerRequestFormValues,
|
||||||
|
attachmentUrls: string[],
|
||||||
|
): DesignRequestInitiateParams => {
|
||||||
|
const { attachments: _attachments, count, ...rest } = values;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
count: count === "" ? undefined : count,
|
||||||
|
attachments: attachmentUrls,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,4 +1,10 @@
|
|||||||
|
import { toast } from "@/components/Toast";
|
||||||
|
import { extractErrorMessage } from "@/helpers/utils";
|
||||||
|
import { toDesignRequestParams } from "@/pages/designer/designerRequestForm";
|
||||||
|
import type { DesignerRequestFormValues } from "@/pages/designer/types/Types";
|
||||||
|
import { useMultipleUpload } from "@/pages/uploader/hooks/useUploaderData";
|
||||||
import { useMutation } from "@tanstack/react-query";
|
import { useMutation } from "@tanstack/react-query";
|
||||||
|
import type { FormikHelpers } from "formik";
|
||||||
import * as api from "../service/DesignerService";
|
import * as api from "../service/DesignerService";
|
||||||
|
|
||||||
export const useInitiateDesignRequest = () => {
|
export const useInitiateDesignRequest = () => {
|
||||||
@@ -6,3 +12,35 @@ export const useInitiateDesignRequest = () => {
|
|||||||
mutationFn: api.initiateDesignRequest,
|
mutationFn: api.initiateDesignRequest,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useSubmitDesignRequest = () => {
|
||||||
|
const { mutateAsync: uploadFiles } = useMultipleUpload();
|
||||||
|
const { mutateAsync: initiateDesignRequest } = useInitiateDesignRequest();
|
||||||
|
|
||||||
|
return async (
|
||||||
|
values: DesignerRequestFormValues,
|
||||||
|
{ setSubmitting, resetForm }: FormikHelpers<DesignerRequestFormValues>,
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const attachmentUrls =
|
||||||
|
values.attachments.length > 0
|
||||||
|
? (await uploadFiles(values.attachments)).data.map((item) => item.url)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const data = await initiateDesignRequest(
|
||||||
|
toDesignRequestParams(values, attachmentUrls),
|
||||||
|
);
|
||||||
|
|
||||||
|
toast("صورت حساب با موفقیت ثبت شد", "success");
|
||||||
|
window.open(
|
||||||
|
`${import.meta.env.VITE_INVOICE_URL}${data?.data?.invoiceId}`,
|
||||||
|
"_blank",
|
||||||
|
);
|
||||||
|
resetForm();
|
||||||
|
} catch (error) {
|
||||||
|
toast(extractErrorMessage(error), "error");
|
||||||
|
} finally {
|
||||||
|
setSubmitting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user