@@ -2,9 +2,7 @@ import axios from "../../../config/axios";
|
|||||||
import { CreateCustomerType } from "../types/CustomerTypes";
|
import { CreateCustomerType } from "../types/CustomerTypes";
|
||||||
|
|
||||||
export const getCustomers = async (page: number, limit: number, withoutPaginate?: boolean, search?: string) => {
|
export const getCustomers = async (page: number, limit: number, withoutPaginate?: boolean, search?: string) => {
|
||||||
const { data } = await axios.get(
|
const { data } = await axios.get(`/users/customers?page=${page}&limit=${limit}&paginate=${withoutPaginate ? "0" : "1"}&q=${encodeURIComponent(search ?? "")}`);
|
||||||
`/users/customers?page=${page}&limit=${limit}&paginate=${withoutPaginate ? "0" : "1"}&q=${encodeURIComponent(search ?? "")}`,
|
|
||||||
);
|
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+248
-283
@@ -1,62 +1,64 @@
|
|||||||
import { ChangeEvent, FC, Fragment, useState } from 'react'
|
import { useFormik } from "formik";
|
||||||
import { useTranslation } from 'react-i18next'
|
import { Add, TickCircle, TickSquare, Trash } from "iconsax-react";
|
||||||
import Button from '../../components/Button'
|
import { ChangeEvent, FC, Fragment, useState } from "react";
|
||||||
import { Add, TickCircle, TickSquare, Trash } from 'iconsax-react'
|
import { useTranslation } from "react-i18next";
|
||||||
import Select from '../../components/Select'
|
import { useNavigate } from "react-router-dom";
|
||||||
import Input from '../../components/Input'
|
import * as Yup from "yup";
|
||||||
import { CreateReceiptType, ReceiptCreateItemsType } from './types/ReceiptTypes'
|
import Button from "../../components/Button";
|
||||||
import { useFormik } from 'formik'
|
import Input from "../../components/Input";
|
||||||
import * as Yup from 'yup'
|
import PageLoading from "../../components/PageLoading";
|
||||||
import { toast } from '../../components/Toast';
|
import Select from "../../components/Select";
|
||||||
import { useGetCustomers } from '../customer/hooks/useCustomerData'
|
import SwitchComponent from "../../components/Switch";
|
||||||
import { CustomerItemType } from '../customer/types/CustomerTypes'
|
import { toast } from "../../components/Toast";
|
||||||
import PageLoading from '../../components/PageLoading'
|
import { Pages } from "../../config/Pages";
|
||||||
import { useCreateInvoice } from './hooks/useReceiptData'
|
import { ErrorType } from "../../helpers/types";
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useGetCustomers } from "../customer/hooks/useCustomerData";
|
||||||
import { Pages } from '../../config/Pages'
|
import { CustomerItemType } from "../customer/types/CustomerTypes";
|
||||||
import { ErrorType } from '../../helpers/types'
|
import { RecurringPeriodEnum } from "./enum/ReceipEnum";
|
||||||
import { RecurringPeriodEnum } from './enum/ReceipEnum'
|
import { useCreateInvoice } from "./hooks/useReceiptData";
|
||||||
import SwitchComponent from '../../components/Switch'
|
import { CreateReceiptType, ReceiptCreateItemsType } from "./types/ReceiptTypes";
|
||||||
const CreateReceipt: FC = () => {
|
const CreateReceipt: FC = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
const navigate = useNavigate()
|
const { t } = useTranslation("global");
|
||||||
const { t } = useTranslation('global')
|
const [items, setItems] = useState<ReceiptCreateItemsType[]>([]);
|
||||||
const [items, setItems] = useState<ReceiptCreateItemsType[]>([])
|
const [customer, setCustomer] = useState<CustomerItemType>();
|
||||||
const [customer, setCustomer] = useState<CustomerItemType>()
|
const [isRecurring, setIsRecurring] = useState<boolean>(false);
|
||||||
const [isRecurring, setIsRecurring] = useState<boolean>(false)
|
const [type, setType] = useState<RecurringPeriodEnum>(RecurringPeriodEnum.WEEKLY);
|
||||||
const [type, setType] = useState<RecurringPeriodEnum>(RecurringPeriodEnum.WEEKLY)
|
const [maxRecurringCycles, setMaxRecurringCycles] = useState<number>(0);
|
||||||
const [maxRecurringCycles, setMaxRecurringCycles] = useState<number>(0)
|
const getCustomers = useGetCustomers(1, 50, true);
|
||||||
const getCustomers = useGetCustomers(1, true)
|
const createInvoice = useCreateInvoice();
|
||||||
const createInvoice = useCreateInvoice()
|
|
||||||
|
|
||||||
const formik = useFormik<ReceiptCreateItemsType>({
|
const formik = useFormik<ReceiptCreateItemsType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
name: '',
|
name: "",
|
||||||
count: '',
|
count: "",
|
||||||
unitPrice: '',
|
unitPrice: "",
|
||||||
discount: ''
|
discount: "",
|
||||||
},
|
},
|
||||||
validationSchema: Yup.object({
|
validationSchema: Yup.object({
|
||||||
name: Yup.string().required(t('errors.required')),
|
name: Yup.string().required(t("errors.required")),
|
||||||
count: Yup.string().required(t('errors.required')),
|
count: Yup.string().required(t("errors.required")),
|
||||||
unitPrice: Yup.string().required(t('errors.required')),
|
unitPrice: Yup.string().required(t("errors.required")),
|
||||||
}),
|
}),
|
||||||
onSubmit: (values) => {
|
onSubmit: (values) => {
|
||||||
setItems((prev) => [...prev, {
|
setItems((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
count: +values.count,
|
count: +values.count,
|
||||||
discount: +values.discount,
|
discount: +values.discount,
|
||||||
name: values.name,
|
name: values.name,
|
||||||
unitPrice: +values.unitPrice
|
unitPrice: +values.unitPrice,
|
||||||
}])
|
},
|
||||||
formik.resetForm()
|
]);
|
||||||
}
|
formik.resetForm();
|
||||||
})
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const handleChangeCustomer = (e: ChangeEvent<HTMLSelectElement>) => {
|
const handleChangeCustomer = (e: ChangeEvent<HTMLSelectElement>) => {
|
||||||
const value = e.target.value
|
const value = e.target.value;
|
||||||
const customer = getCustomers.data?.data?.customers?.find((item: CustomerItemType) => item.id === value)
|
const customer = getCustomers.data?.data?.customers?.find((item: CustomerItemType) => item.id === value);
|
||||||
setCustomer(customer)
|
setCustomer(customer);
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
if (customer) {
|
if (customer) {
|
||||||
@@ -65,406 +67,369 @@ const CreateReceipt: FC = () => {
|
|||||||
items: items,
|
items: items,
|
||||||
isRecurring: isRecurring,
|
isRecurring: isRecurring,
|
||||||
recurringPeriod: isRecurring ? +type : undefined,
|
recurringPeriod: isRecurring ? +type : undefined,
|
||||||
maxRecurringCycles: isRecurring ? maxRecurringCycles : undefined
|
maxRecurringCycles: isRecurring ? maxRecurringCycles : undefined,
|
||||||
}
|
};
|
||||||
|
|
||||||
createInvoice.mutate(params, {
|
createInvoice.mutate(params, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast(t('success'), 'success')
|
toast(t("success"), "success");
|
||||||
navigate(Pages.receipts.index)
|
navigate(Pages.receipts.index);
|
||||||
},
|
},
|
||||||
onError(error: ErrorType) {
|
onError(error: ErrorType) {
|
||||||
toast(error.response?.data?.error.message[0], 'error')
|
toast(error.response?.data?.error.message[0], "error");
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleRemove = (index: number) => {
|
const handleRemove = (index: number) => {
|
||||||
setItems((prev) => prev.filter((_, i) => i !== index))
|
setItems((prev) => prev.filter((_, i) => i !== index));
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-4'>
|
<div className="mt-4">
|
||||||
{
|
{getCustomers.isPending ? (
|
||||||
getCustomers.isPending ?
|
|
||||||
<PageLoading />
|
<PageLoading />
|
||||||
:
|
) : (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className='flex justify-between items-center'>
|
<div className="flex justify-between items-center">
|
||||||
<div>
|
<div>{t("receip.add_receip")}</div>
|
||||||
{t('receip.add_receip')}
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
className='px-5'
|
className="px-5"
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
isLoading={createInvoice.isPending}
|
isLoading={createInvoice.isPending}
|
||||||
>
|
>
|
||||||
<div className='flex gap-2 items-center'>
|
<div className="flex gap-2 items-center">
|
||||||
<TickCircle size={20} color='white' />
|
<TickCircle
|
||||||
<div>
|
size={20}
|
||||||
{t('receip.submit_receip')}
|
color="white"
|
||||||
</div>
|
/>
|
||||||
|
<div>{t("receip.submit_receip")}</div>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex gap-6 xl:mt-8 mt-4'>
|
<div className="flex gap-6 xl:mt-8 mt-4">
|
||||||
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
<div className="flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl">
|
||||||
<div>
|
<div>{t("receip.customer_information")}</div>
|
||||||
{t('receip.customer_information')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-8'>
|
<div className="mt-8">
|
||||||
<div className='w-full xl:w-1/2'>
|
<div className="w-full xl:w-1/2">
|
||||||
<Select
|
<Select
|
||||||
label={t('receip.customer')}
|
label={t("receip.customer")}
|
||||||
placeholder={t('select')}
|
placeholder={t("select")}
|
||||||
items={getCustomers.data?.data?.customers?.map((item: CustomerItemType) => {
|
items={getCustomers.data?.data?.customers?.map((item: CustomerItemType) => {
|
||||||
return {
|
return {
|
||||||
label: item.firstName + ' ' + item.lastName,
|
label: item.firstName + " " + item.lastName,
|
||||||
value: item.id
|
value: item.id,
|
||||||
}
|
};
|
||||||
})}
|
})}
|
||||||
onChange={handleChangeCustomer}
|
onChange={handleChangeCustomer}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{customer && (
|
||||||
customer &&
|
<div className="mt-10 bg-[#F6F7FB] p-8 rounded-3xl text-sm">
|
||||||
<div className='mt-10 bg-[#F6F7FB] p-8 rounded-3xl text-sm'>
|
<div className="flex justify-between items-center border-b border-border border-dashed pb-7">
|
||||||
<div className='flex justify-between items-center border-b border-border border-dashed pb-7'>
|
<div className="flex items-center gap-2">
|
||||||
<div className='flex items-center gap-2'>
|
<div className="text-description">{t("receip.type_person")}</div>
|
||||||
<div className='text-description'>{t('receip.type_person')}</div>
|
<div>{customer.legalUser ? "حقوقی" : "حقیقی"}</div>
|
||||||
<div>{customer.legalUser ? 'حقوقی' : 'حقیقی'}</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2'>
|
<div className="flex items-center gap-2">
|
||||||
<div className='text-description'>{t('receip.representativeـname')}</div>
|
<div className="text-description">{t("receip.representativeـname")}</div>
|
||||||
<div>{customer.firstName + ' ' + customer.lastName}</div>
|
<div>{customer.firstName + " " + customer.lastName}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2'>
|
<div className="flex items-center gap-2">
|
||||||
<div className='text-description'>{t('receip.company_name')}</div>
|
<div className="text-description">{t("receip.company_name")}</div>
|
||||||
<div>{customer?.legalUser?.registrationName}</div>
|
<div>{customer?.legalUser?.registrationName}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2'>
|
<div className="flex items-center gap-2">
|
||||||
<div className='text-description'>{t('receip.registration_number')}</div>
|
<div className="text-description">{t("receip.registration_number")}</div>
|
||||||
<div>{customer?.legalUser?.registrationCode}</div>
|
<div>{customer?.legalUser?.registrationCode}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-between items-center mt-7'>
|
<div className="flex justify-between items-center mt-7">
|
||||||
<div className='flex items-center gap-2 flex-1'>
|
<div className="flex items-center gap-2 flex-1">
|
||||||
<div className='text-description'>{t('receip.tel_company')}</div>
|
<div className="text-description">{t("receip.tel_company")}</div>
|
||||||
<div>{customer?.realUser?.phone || customer?.legalUser?.phone}</div>
|
<div>{customer?.realUser?.phone || customer?.legalUser?.phone}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2 flex-1'>
|
<div className="flex items-center gap-2 flex-1">
|
||||||
<div className='text-description'>{t('receip.national_code')}</div>
|
<div className="text-description">{t("receip.national_code")}</div>
|
||||||
<div>{customer.nationalCode}</div>
|
<div>{customer.nationalCode}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2 flex-1'>
|
<div className="flex items-center gap-2 flex-1">
|
||||||
<div className='text-description'>{t('receip.postal_code')}</div>
|
<div className="text-description">{t("receip.postal_code")}</div>
|
||||||
<div>{customer?.realUser?.address?.postalCode || customer?.legalUser?.address?.postalCode}</div>
|
<div>{customer?.realUser?.address?.postalCode || customer?.legalUser?.address?.postalCode}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-between items-center mt-7'>
|
<div className="flex justify-between items-center mt-7">
|
||||||
<div className='flex items-center gap-2 flex-1'>
|
<div className="flex items-center gap-2 flex-1">
|
||||||
<div className='text-description'>{t('receip.state')}</div>
|
<div className="text-description">{t("receip.state")}</div>
|
||||||
<div>{customer?.realUser?.address?.city?.province?.name || customer?.legalUser?.address?.city?.province?.name}</div>
|
<div>{customer?.realUser?.address?.city?.province?.name || customer?.legalUser?.address?.city?.province?.name}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2 flex-1'>
|
<div className="flex items-center gap-2 flex-1">
|
||||||
<div className='text-description'>{t('receip.city')}</div>
|
<div className="text-description">{t("receip.city")}</div>
|
||||||
<div>{customer?.realUser?.address?.city?.name || customer?.legalUser?.address?.city?.name}</div>
|
<div>{customer?.realUser?.address?.city?.name || customer?.legalUser?.address?.city?.name}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2 flex-1'>
|
<div className="flex items-center gap-2 flex-1">
|
||||||
<div className='text-description'>{t('receip.address')}</div>
|
<div className="text-description">{t("receip.address")}</div>
|
||||||
<div>{customer?.realUser?.address?.fullAddress || customer?.legalUser?.address?.fullAddress}</div>
|
<div>{customer?.realUser?.address?.fullAddress || customer?.legalUser?.address?.fullAddress}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
|
|
||||||
<div className='mt-10'>
|
<div className="mt-10">
|
||||||
<SwitchComponent
|
<SwitchComponent
|
||||||
label={t('receip.recurring')}
|
label={t("receip.recurring")}
|
||||||
active={isRecurring}
|
active={isRecurring}
|
||||||
onChange={(value) => setIsRecurring(value)}
|
onChange={(value) => setIsRecurring(value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{isRecurring && (
|
||||||
isRecurring &&
|
<div className="mt-10">
|
||||||
<div className='mt-10'>
|
<div>{t("receip.select_type")}</div>
|
||||||
<div>
|
|
||||||
{t('receip.select_type')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-4 rowTwoInput'>
|
<div className="mt-4 rowTwoInput">
|
||||||
<Select
|
<Select
|
||||||
label={t('select')}
|
label={t("select")}
|
||||||
items={[
|
items={[
|
||||||
{ label: t('receip.WEEKLY'), value: RecurringPeriodEnum.WEEKLY.toString() },
|
{ label: t("receip.WEEKLY"), value: RecurringPeriodEnum.WEEKLY.toString() },
|
||||||
{ label: t('receip.MONTHLY'), value: RecurringPeriodEnum.MONTHLY.toString() },
|
{ label: t("receip.MONTHLY"), value: RecurringPeriodEnum.MONTHLY.toString() },
|
||||||
{ label: t('receip.QUARTERLY'), value: RecurringPeriodEnum.QUARTERLY.toString() },
|
{ label: t("receip.QUARTERLY"), value: RecurringPeriodEnum.QUARTERLY.toString() },
|
||||||
{ label: t('receip.SEMIANNUALLY'), value: RecurringPeriodEnum.SEMIANNUALLY.toString() },
|
{ label: t("receip.SEMIANNUALLY"), value: RecurringPeriodEnum.SEMIANNUALLY.toString() },
|
||||||
{ label: t('receip.ANNUALLY'), value: RecurringPeriodEnum.ANNUALLY.toString() }
|
{ label: t("receip.ANNUALLY"), value: RecurringPeriodEnum.ANNUALLY.toString() },
|
||||||
]}
|
]}
|
||||||
onChange={(e) => setType(e.target.value as unknown as RecurringPeriodEnum)}
|
onChange={(e) => setType(e.target.value as unknown as RecurringPeriodEnum)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
label={t('receip.maxRecurringCycles')}
|
label={t("receip.maxRecurringCycles")}
|
||||||
name='maxRecurringCycles'
|
name="maxRecurringCycles"
|
||||||
value={maxRecurringCycles}
|
value={maxRecurringCycles}
|
||||||
onChange={(e) => setMaxRecurringCycles(+e.target.value)}
|
onChange={(e) => setMaxRecurringCycles(+e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
|
|
||||||
<div className='mt-10'>
|
<div className="mt-10">
|
||||||
<div>{t('receip.receipt_information')}</div>
|
<div>{t("receip.receipt_information")}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-8 border-b border-dashed border-border pb-7 flex items-end justify-between text-xs text-description'>
|
<div className="mt-8 border-b border-dashed border-border pb-7 flex items-end justify-between text-xs text-description">
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
<div>
|
<div>{t("receip.number")}</div>
|
||||||
{t('receip.number')}
|
<div className="size-10 bg-[#EBEDF5] rounded-xl flex justify-center items-center">{items.length + 1}</div>
|
||||||
</div>
|
|
||||||
<div className='size-10 bg-[#EBEDF5] rounded-xl flex justify-center items-center'>
|
|
||||||
{items.length + 1}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
<div>
|
<div>{t("receip.product_name")}</div>
|
||||||
{t('receip.product_name')}
|
|
||||||
</div>
|
|
||||||
<Input
|
<Input
|
||||||
className='text-center'
|
className="text-center"
|
||||||
name='name'
|
name="name"
|
||||||
value={formik.values.name}
|
value={formik.values.name}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
<div>
|
<div>{t("receip.count")}</div>
|
||||||
{t('receip.count')}
|
|
||||||
</div>
|
|
||||||
<Input
|
<Input
|
||||||
type='number'
|
type="number"
|
||||||
className='w-16 text-center'
|
className="w-16 text-center"
|
||||||
name='count'
|
name="count"
|
||||||
value={formik.values.count}
|
value={formik.values.count}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
<div>
|
<div>{t("receip.unit_amount")}</div>
|
||||||
{t('receip.unit_amount')}
|
|
||||||
</div>
|
|
||||||
<Input
|
<Input
|
||||||
className='text-center'
|
className="text-center"
|
||||||
name='unitPrice'
|
name="unitPrice"
|
||||||
value={formik.values.unitPrice}
|
value={formik.values.unitPrice}
|
||||||
onChange={(e) => formik.setFieldValue('unitPrice', e.target.value)}
|
onChange={(e) => formik.setFieldValue("unitPrice", e.target.value)}
|
||||||
seprator
|
seprator
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
<div>
|
<div>{t("receip.discount")}</div>
|
||||||
{t('receip.discount')}
|
|
||||||
</div>
|
|
||||||
<Input
|
<Input
|
||||||
type='number'
|
type="number"
|
||||||
className='w-16 text-center'
|
className="w-16 text-center"
|
||||||
name='discount'
|
name="discount"
|
||||||
value={formik.values.discount}
|
value={formik.values.discount}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
<div>
|
<div>{t("receip.total_amount")}</div>
|
||||||
{t('receip.total_amount')}
|
|
||||||
</div>
|
|
||||||
<Input
|
<Input
|
||||||
className='text-center bg-[#EBEDF5]'
|
className="text-center bg-[#EBEDF5]"
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div onClick={() => {
|
<div
|
||||||
|
onClick={() => {
|
||||||
if (formik.errors.name || formik.errors.count || formik.errors.unitPrice || formik.errors.discount) {
|
if (formik.errors.name || formik.errors.count || formik.errors.unitPrice || formik.errors.discount) {
|
||||||
toast(t('receip.error_empty'), 'error')
|
toast(t("receip.error_empty"), "error");
|
||||||
} else {
|
} else {
|
||||||
formik.handleSubmit()
|
formik.handleSubmit();
|
||||||
}
|
}
|
||||||
}} className='size-10 border border-border rounded-xl flex justify-center items-center'>
|
}}
|
||||||
<Add size={20} color='black' />
|
className="size-10 border border-border rounded-xl flex justify-center items-center"
|
||||||
|
>
|
||||||
|
<Add
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{items.map((item, index: number) => {
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
|
||||||
items.map((item, index: number) => {
|
|
||||||
return (
|
return (
|
||||||
<div key={item.name} className='mt-6 flex items-end justify-between text-xs text-description'>
|
<div
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
key={item.name}
|
||||||
{
|
className="mt-6 flex items-end justify-between text-xs text-description"
|
||||||
index === 0 &&
|
>
|
||||||
<div>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
{t('receip.number')}
|
{index === 0 && <div>{t("receip.number")}</div>}
|
||||||
|
<div className="size-10 bg-[#EBEDF5] rounded-xl flex justify-center items-center">{index + 1}</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
<div className="flex flex-col gap-2 items-center">
|
||||||
<div className='size-10 bg-[#EBEDF5] rounded-xl flex justify-center items-center'>
|
{index === 0 && <div>{t("receip.product_name")}</div>}
|
||||||
{index + 1}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
|
||||||
{
|
|
||||||
index === 0 &&
|
|
||||||
<div>
|
|
||||||
{t('receip.product_name')}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<Input
|
<Input
|
||||||
className='text-center'
|
className="text-center"
|
||||||
name='name'
|
name="name"
|
||||||
readOnly
|
readOnly
|
||||||
value={item.name}
|
value={item.name}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
{
|
{index === 0 && <div>{t("receip.count")}</div>}
|
||||||
index === 0 &&
|
|
||||||
<div>
|
|
||||||
{t('receip.count')}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<Input
|
<Input
|
||||||
type='number'
|
type="number"
|
||||||
className='w-16 text-center'
|
className="w-16 text-center"
|
||||||
name='count'
|
name="count"
|
||||||
readOnly
|
readOnly
|
||||||
value={item.count}
|
value={item.count}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
{
|
{index === 0 && <div>{t("receip.unit_amount")}</div>}
|
||||||
index === 0 &&
|
|
||||||
<div>
|
|
||||||
{t('receip.unit_amount')}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<Input
|
<Input
|
||||||
className='text-center'
|
className="text-center"
|
||||||
name='unitPrice'
|
name="unitPrice"
|
||||||
readOnly
|
readOnly
|
||||||
value={item.unitPrice}
|
value={item.unitPrice}
|
||||||
seprator
|
seprator
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
{
|
{index === 0 && <div>{t("receip.discount")}</div>}
|
||||||
index === 0 &&
|
|
||||||
<div>
|
|
||||||
{t('receip.discount')}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<Input
|
<Input
|
||||||
type='number'
|
type="number"
|
||||||
className='w-16 text-center'
|
className="w-16 text-center"
|
||||||
name='discount'
|
name="discount"
|
||||||
readOnly
|
readOnly
|
||||||
value={item.discount}
|
value={item.discount}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
{
|
{index === 0 && <div>{t("receip.total_amount")}</div>}
|
||||||
index === 0 &&
|
|
||||||
<div>
|
|
||||||
{t('receip.total_amount')}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<Input
|
<Input
|
||||||
className='text-center bg-[#EBEDF5]'
|
className="text-center bg-[#EBEDF5]"
|
||||||
readOnly
|
readOnly
|
||||||
value={((Number(item.unitPrice) || 0) * (Number(item.count) || 0) - (Number(item.unitPrice) || 0) * (Number(item.count) || 0) * (Number(item.discount) / 100 || 0)).toFixed(1)}
|
value={((Number(item.unitPrice) || 0) * (Number(item.count) || 0) - (Number(item.unitPrice) || 0) * (Number(item.count) || 0) * (Number(item.discount) / 100 || 0)).toFixed(1)}
|
||||||
seprator
|
seprator
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div onClick={() => handleRemove(index)} className='size-10 border border-border rounded-xl flex justify-center items-center'>
|
<div
|
||||||
<Trash size={20} color='black' />
|
onClick={() => handleRemove(index)}
|
||||||
|
className="size-10 border border-border rounded-xl flex justify-center items-center"
|
||||||
|
>
|
||||||
|
<Trash
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
})
|
})}
|
||||||
}
|
|
||||||
|
|
||||||
|
<div className="mt-8 flex flex-col gap-4 items-end">
|
||||||
|
<div className="xl:w-1/2 h-12 w-full rounded-xl flex px-4 bg-[#EBEDF5] text-sm text-description justify-between items-center">
|
||||||
<div className='mt-8 flex flex-col gap-4 items-end'>
|
<div>{t("receip.tax")}</div>
|
||||||
<div className='xl:w-1/2 h-12 w-full rounded-xl flex px-4 bg-[#EBEDF5] text-sm text-description justify-between items-center'>
|
<div className="text-black">
|
||||||
<div>
|
|
||||||
{t('receip.tax')}
|
|
||||||
</div>
|
|
||||||
<div className='text-black'>
|
|
||||||
{((items.reduce((acc, item) => acc + (Number(item.unitPrice) || 0) * (Number(item.count) || 0) - (Number(item.unitPrice) || 0) * (Number(item.count) || 0) * (Number(item.discount) / 100 || 0), 0)) * 0.10).toFixed(1)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='xl:w-1/2 h-12 w-full rounded-xl flex px-4 bg-[#EBEDF5] text-sm text-description justify-between items-center'>
|
|
||||||
<div>
|
|
||||||
{t('receip.total')}
|
|
||||||
</div>
|
|
||||||
<div className='text-black'>
|
|
||||||
{(
|
{(
|
||||||
items.reduce((acc, item) => acc + (Number(item.unitPrice) || 0) * (Number(item.count) || 0) - (Number(item.unitPrice) || 0) * (Number(item.count) || 0) * (Number(item.discount) / 100 || 0), 0) * 1.10
|
items.reduce(
|
||||||
|
(acc, item) => acc + (Number(item.unitPrice) || 0) * (Number(item.count) || 0) - (Number(item.unitPrice) || 0) * (Number(item.count) || 0) * (Number(item.discount) / 100 || 0),
|
||||||
|
0,
|
||||||
|
) * 0.1
|
||||||
|
).toFixed(1)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="xl:w-1/2 h-12 w-full rounded-xl flex px-4 bg-[#EBEDF5] text-sm text-description justify-between items-center">
|
||||||
|
<div>{t("receip.total")}</div>
|
||||||
|
<div className="text-black">
|
||||||
|
{(
|
||||||
|
items.reduce(
|
||||||
|
(acc, item) => acc + (Number(item.unitPrice) || 0) * (Number(item.count) || 0) - (Number(item.unitPrice) || 0) * (Number(item.count) || 0) * (Number(item.discount) / 100 || 0),
|
||||||
|
0,
|
||||||
|
) * 1.1
|
||||||
).toFixed(1)}
|
).toFixed(1)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='bg-white w-sidebar 3xl:block hidden py-10 px-5 h-fit rounded-3xl'>
|
<div className="bg-white w-sidebar 3xl:block hidden py-10 px-5 h-fit rounded-3xl">
|
||||||
<div className='text-sm'>
|
<div className="text-sm">{t("ticket.title_hint")}</div>
|
||||||
{t('ticket.title_hint')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-6'>
|
<div className="mt-6">
|
||||||
<div className='flex items-start gap-2 border-b pb-5'>
|
<div className="flex items-start gap-2 border-b pb-5">
|
||||||
<div>
|
<div>
|
||||||
<TickSquare size={20} color='black' variant='Bold' />
|
<TickSquare
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
variant="Bold"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-description text-xs leading-5'>
|
<div className="text-description text-xs leading-5">سوالات - مشکلاتی که به یک موضوع مربوط میشوند را در یک درخواست پشتیبانی پیگیر باشید و چند درخواست برای یک موضوع باز نکنید.</div>
|
||||||
سوالات - مشکلاتی که به یک موضوع مربوط میشوند را در یک درخواست پشتیبانی پیگیر باشید و چند درخواست برای یک موضوع باز نکنید.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex items-start gap-2 mt-6 border-b pb-5">
|
||||||
<div className='flex items-start gap-2 mt-6 border-b pb-5'>
|
|
||||||
<div>
|
<div>
|
||||||
<TickSquare size={20} color='black' variant='Bold' />
|
<TickSquare
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
variant="Bold"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-description text-xs leading-5'>
|
<div className="text-description text-xs leading-5">لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.</div>
|
||||||
لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex items-start gap-2 mt-6">
|
||||||
<div className='flex items-start gap-2 mt-6'>
|
|
||||||
<div>
|
<div>
|
||||||
<TickSquare size={20} color='black' variant='Bold' />
|
<TickSquare
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
variant="Bold"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-description text-xs leading-5'>
|
<div className="text-description text-xs leading-5">
|
||||||
پاسخگویی 24 ساعته تلفنی را تنها از میهن وب هاست می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
|
پاسخگویی 24 ساعته تلفنی را تنها از میهن وب هاست می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -472,9 +437,9 @@ const CreateReceipt: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default CreateReceipt
|
export default CreateReceipt;
|
||||||
|
|||||||
+263
-296
@@ -1,22 +1,22 @@
|
|||||||
import { ChangeEvent, FC, Fragment, useState, useEffect } from 'react'
|
import { useFormik } from "formik";
|
||||||
import { useTranslation } from 'react-i18next'
|
import { Add, TickCircle, TickSquare, Trash } from "iconsax-react";
|
||||||
import Button from '../../components/Button'
|
import { ChangeEvent, FC, Fragment, useEffect, useState } from "react";
|
||||||
import { Add, TickCircle, TickSquare, Trash } from 'iconsax-react'
|
import { useTranslation } from "react-i18next";
|
||||||
import Select from '../../components/Select'
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import Input from '../../components/Input'
|
import * as Yup from "yup";
|
||||||
import { CreateReceiptType, ReceiptCreateItemsType } from './types/ReceiptTypes'
|
import Button from "../../components/Button";
|
||||||
import { useFormik } from 'formik'
|
import Input from "../../components/Input";
|
||||||
import * as Yup from 'yup'
|
import PageLoading from "../../components/PageLoading";
|
||||||
import { toast } from '../../components/Toast';
|
import Select from "../../components/Select";
|
||||||
import { useGetCustomers } from '../customer/hooks/useCustomerData'
|
import SwitchComponent from "../../components/Switch";
|
||||||
import { CustomerItemType } from '../customer/types/CustomerTypes'
|
import { toast } from "../../components/Toast";
|
||||||
import PageLoading from '../../components/PageLoading'
|
import { Pages } from "../../config/Pages";
|
||||||
import { useGetInvoiceById, useUpdateInvoice } from './hooks/useReceiptData'
|
import { ErrorType } from "../../helpers/types";
|
||||||
import { useNavigate, useParams } from 'react-router-dom'
|
import { useGetCustomers } from "../customer/hooks/useCustomerData";
|
||||||
import { Pages } from '../../config/Pages'
|
import { CustomerItemType } from "../customer/types/CustomerTypes";
|
||||||
import { ErrorType } from '../../helpers/types'
|
import { RecurringPeriodEnum } from "./enum/ReceipEnum";
|
||||||
import { RecurringPeriodEnum } from './enum/ReceipEnum'
|
import { useGetInvoiceById, useUpdateInvoice } from "./hooks/useReceiptData";
|
||||||
import SwitchComponent from '../../components/Switch'
|
import { CreateReceiptType, ReceiptCreateItemsType } from "./types/ReceiptTypes";
|
||||||
|
|
||||||
// Add interface for invoice item from API
|
// Add interface for invoice item from API
|
||||||
interface InvoiceItemType {
|
interface InvoiceItemType {
|
||||||
@@ -32,47 +32,49 @@ interface InvoiceItemType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const CreateReceipt: FC = () => {
|
const CreateReceipt: FC = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
const navigate = useNavigate()
|
const { id } = useParams();
|
||||||
const { id } = useParams()
|
const { t } = useTranslation("global");
|
||||||
const { t } = useTranslation('global')
|
const { data: invoice, isPending: isLoading } = useGetInvoiceById(id as string);
|
||||||
const { data: invoice, isPending: isLoading } = useGetInvoiceById(id as string)
|
const updateInvoice = useUpdateInvoice();
|
||||||
const updateInvoice = useUpdateInvoice()
|
const [items, setItems] = useState<ReceiptCreateItemsType[]>([]);
|
||||||
const [items, setItems] = useState<ReceiptCreateItemsType[]>([])
|
const [customer, setCustomer] = useState<CustomerItemType>();
|
||||||
const [customer, setCustomer] = useState<CustomerItemType>()
|
const [isRecurring, setIsRecurring] = useState<boolean>(false);
|
||||||
const [isRecurring, setIsRecurring] = useState<boolean>(false)
|
const [type, setType] = useState<RecurringPeriodEnum>(RecurringPeriodEnum.WEEKLY);
|
||||||
const [type, setType] = useState<RecurringPeriodEnum>(RecurringPeriodEnum.WEEKLY)
|
const [maxRecurringCycles, setMaxRecurringCycles] = useState<number>(0);
|
||||||
const [maxRecurringCycles, setMaxRecurringCycles] = useState<number>(0)
|
const getCustomers = useGetCustomers(1, 50, true);
|
||||||
const getCustomers = useGetCustomers(1, true)
|
|
||||||
|
|
||||||
const formik = useFormik<ReceiptCreateItemsType>({
|
const formik = useFormik<ReceiptCreateItemsType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
name: '',
|
name: "",
|
||||||
count: '',
|
count: "",
|
||||||
unitPrice: '',
|
unitPrice: "",
|
||||||
discount: ''
|
discount: "",
|
||||||
},
|
},
|
||||||
validationSchema: Yup.object({
|
validationSchema: Yup.object({
|
||||||
name: Yup.string().required(t('errors.required')),
|
name: Yup.string().required(t("errors.required")),
|
||||||
count: Yup.string().required(t('errors.required')),
|
count: Yup.string().required(t("errors.required")),
|
||||||
unitPrice: Yup.string().required(t('errors.required')),
|
unitPrice: Yup.string().required(t("errors.required")),
|
||||||
}),
|
}),
|
||||||
onSubmit: (values) => {
|
onSubmit: (values) => {
|
||||||
setItems((prev) => [...prev, {
|
setItems((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
count: +values.count,
|
count: +values.count,
|
||||||
discount: +values.discount,
|
discount: +values.discount,
|
||||||
name: values.name,
|
name: values.name,
|
||||||
unitPrice: +values.unitPrice
|
unitPrice: +values.unitPrice,
|
||||||
}])
|
},
|
||||||
formik.resetForm()
|
]);
|
||||||
}
|
formik.resetForm();
|
||||||
})
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const handleChangeCustomer = (e: ChangeEvent<HTMLSelectElement>) => {
|
const handleChangeCustomer = (e: ChangeEvent<HTMLSelectElement>) => {
|
||||||
const value = e.target.value
|
const value = e.target.value;
|
||||||
const customer = getCustomers.data?.data?.customers?.find((item: CustomerItemType) => item.id === value)
|
const customer = getCustomers.data?.data?.customers?.find((item: CustomerItemType) => item.id === value);
|
||||||
setCustomer(customer)
|
setCustomer(customer);
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
if (customer) {
|
if (customer) {
|
||||||
@@ -81,28 +83,30 @@ const CreateReceipt: FC = () => {
|
|||||||
items: items,
|
items: items,
|
||||||
isRecurring: isRecurring,
|
isRecurring: isRecurring,
|
||||||
recurringPeriod: isRecurring ? +type : undefined,
|
recurringPeriod: isRecurring ? +type : undefined,
|
||||||
maxRecurringCycles: isRecurring ? maxRecurringCycles : undefined
|
maxRecurringCycles: isRecurring ? maxRecurringCycles : undefined,
|
||||||
}
|
};
|
||||||
|
|
||||||
updateInvoice.mutate({ id: id as string, params }, {
|
updateInvoice.mutate(
|
||||||
|
{ id: id as string, params },
|
||||||
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast(t('success'), 'success')
|
toast(t("success"), "success");
|
||||||
navigate(Pages.receipts.index)
|
navigate(Pages.receipts.index);
|
||||||
},
|
},
|
||||||
onError(error: ErrorType) {
|
onError(error: ErrorType) {
|
||||||
toast(error.response?.data?.error.message[0], 'error')
|
toast(error.response?.data?.error.message[0], "error");
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleRemove = (index: number) => {
|
const handleRemove = (index: number) => {
|
||||||
setItems((prev) => prev.filter((_, i) => i !== index))
|
setItems((prev) => prev.filter((_, i) => i !== index));
|
||||||
}
|
};
|
||||||
|
|
||||||
// Prefill form when editing an invoice
|
// Prefill form when editing an invoice
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
// Check if we need to access invoice.data or directly invoice
|
// Check if we need to access invoice.data or directly invoice
|
||||||
const invoiceData = invoice?.data?.invoice || invoice;
|
const invoiceData = invoice?.data?.invoice || invoice;
|
||||||
|
|
||||||
@@ -111,9 +115,7 @@ const CreateReceipt: FC = () => {
|
|||||||
|
|
||||||
// 1. If there's a user, find matching customer
|
// 1. If there's a user, find matching customer
|
||||||
if (invoiceData.user && getCustomers.data?.data?.customers) {
|
if (invoiceData.user && getCustomers.data?.data?.customers) {
|
||||||
const foundCustomer = getCustomers.data.data.customers.find(
|
const foundCustomer = getCustomers.data.data.customers.find((item: CustomerItemType) => item.id === invoiceData.user.id);
|
||||||
(item: CustomerItemType) => item.id === invoiceData.user.id
|
|
||||||
);
|
|
||||||
setCustomer(foundCustomer);
|
setCustomer(foundCustomer);
|
||||||
console.log("Found customer:", foundCustomer);
|
console.log("Found customer:", foundCustomer);
|
||||||
setCustomer(foundCustomer);
|
setCustomer(foundCustomer);
|
||||||
@@ -123,20 +125,21 @@ const CreateReceipt: FC = () => {
|
|||||||
if (invoiceData.items && invoiceData.items.length > 0) {
|
if (invoiceData.items && invoiceData.items.length > 0) {
|
||||||
console.log("Setting items:", invoiceData.items);
|
console.log("Setting items:", invoiceData.items);
|
||||||
// Map API items to the format expected by our component
|
// Map API items to the format expected by our component
|
||||||
setItems(invoiceData.items.map((item: InvoiceItemType) => ({
|
setItems(
|
||||||
|
invoiceData.items.map((item: InvoiceItemType) => ({
|
||||||
name: item.name,
|
name: item.name,
|
||||||
count: item.count,
|
count: item.count,
|
||||||
unitPrice: item.unitPrice,
|
unitPrice: item.unitPrice,
|
||||||
discount: item.discount || 0
|
discount: item.discount || 0,
|
||||||
})));
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
// 3. Set formik values from first item
|
// 3. Set formik values from first item
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Set recurring settings
|
// 4. Set recurring settings
|
||||||
setIsRecurring(Boolean(invoiceData.isRecurring));
|
setIsRecurring(Boolean(invoiceData.isRecurring));
|
||||||
if (typeof invoiceData.recurringPeriod === 'number') {
|
if (typeof invoiceData.recurringPeriod === "number") {
|
||||||
setType(invoiceData.recurringPeriod.toString());
|
setType(invoiceData.recurringPeriod.toString());
|
||||||
}
|
}
|
||||||
setMaxRecurringCycles(invoiceData.maxRecurringCycles || 0);
|
setMaxRecurringCycles(invoiceData.maxRecurringCycles || 0);
|
||||||
@@ -149,8 +152,8 @@ const CreateReceipt: FC = () => {
|
|||||||
// Trigger the onChange manually with a synthetic event
|
// Trigger the onChange manually with a synthetic event
|
||||||
const event = {
|
const event = {
|
||||||
target: {
|
target: {
|
||||||
value: invoice.user.id
|
value: invoice.user.id,
|
||||||
}
|
},
|
||||||
} as ChangeEvent<HTMLSelectElement>;
|
} as ChangeEvent<HTMLSelectElement>;
|
||||||
|
|
||||||
handleChangeCustomer(event);
|
handleChangeCustomer(event);
|
||||||
@@ -166,387 +169,351 @@ const CreateReceipt: FC = () => {
|
|||||||
}, [customer, invoice]);
|
}, [customer, invoice]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-4'>
|
<div className="mt-4">
|
||||||
{
|
{getCustomers.isPending || isLoading ? (
|
||||||
getCustomers.isPending || isLoading ?
|
|
||||||
<PageLoading />
|
<PageLoading />
|
||||||
:
|
) : (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className='flex justify-between items-center'>
|
<div className="flex justify-between items-center">
|
||||||
<div>
|
<div>{t("receip.add_receip")}</div>
|
||||||
{t('receip.add_receip')}
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
className='px-5'
|
className="px-5"
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
isLoading={updateInvoice.isPending}
|
isLoading={updateInvoice.isPending}
|
||||||
>
|
>
|
||||||
<div className='flex gap-2 items-center'>
|
<div className="flex gap-2 items-center">
|
||||||
<TickCircle size={20} color='white' />
|
<TickCircle
|
||||||
<div>
|
size={20}
|
||||||
{t('receip.submit_receip')}
|
color="white"
|
||||||
</div>
|
/>
|
||||||
|
<div>{t("receip.submit_receip")}</div>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex gap-6 xl:mt-8 mt-4'>
|
<div className="flex gap-6 xl:mt-8 mt-4">
|
||||||
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
<div className="flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl">
|
||||||
<div>
|
<div>{t("receip.customer_information")}</div>
|
||||||
{t('receip.customer_information')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-8'>
|
<div className="mt-8">
|
||||||
<div className='w-full xl:w-1/2'>
|
<div className="w-full xl:w-1/2">
|
||||||
<Select
|
<Select
|
||||||
label={t('receip.customer')}
|
label={t("receip.customer")}
|
||||||
placeholder={t('select')}
|
placeholder={t("select")}
|
||||||
value={(invoice?.data?.invoice || invoice)?.user?.id}
|
value={(invoice?.data?.invoice || invoice)?.user?.id}
|
||||||
items={getCustomers.data?.data?.customers?.map((item: CustomerItemType) => {
|
items={getCustomers.data?.data?.customers?.map((item: CustomerItemType) => {
|
||||||
return {
|
return {
|
||||||
label: item.firstName + ' ' + item.lastName,
|
label: item.firstName + " " + item.lastName,
|
||||||
value: item.id
|
value: item.id,
|
||||||
}
|
};
|
||||||
})}
|
})}
|
||||||
onChange={handleChangeCustomer}
|
onChange={handleChangeCustomer}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{customer && (
|
||||||
customer &&
|
<div className="mt-10 bg-[#F6F7FB] p-8 rounded-3xl text-sm">
|
||||||
<div className='mt-10 bg-[#F6F7FB] p-8 rounded-3xl text-sm'>
|
<div className="flex justify-between items-center border-b border-border border-dashed pb-7">
|
||||||
<div className='flex justify-between items-center border-b border-border border-dashed pb-7'>
|
<div className="flex items-center gap-2">
|
||||||
<div className='flex items-center gap-2'>
|
<div className="text-description">{t("receip.type_person")}</div>
|
||||||
<div className='text-description'>{t('receip.type_person')}</div>
|
<div>{customer.legalUser ? "حقوقی" : "حقیقی"}</div>
|
||||||
<div>{customer.legalUser ? 'حقوقی' : 'حقیقی'}</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2'>
|
<div className="flex items-center gap-2">
|
||||||
<div className='text-description'>{t('receip.representativeـname')}</div>
|
<div className="text-description">{t("receip.representativeـname")}</div>
|
||||||
<div>{customer.firstName + ' ' + customer.lastName}</div>
|
<div>{customer.firstName + " " + customer.lastName}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2'>
|
<div className="flex items-center gap-2">
|
||||||
<div className='text-description'>{t('receip.company_name')}</div>
|
<div className="text-description">{t("receip.company_name")}</div>
|
||||||
<div>{customer?.legalUser?.registrationName}</div>
|
<div>{customer?.legalUser?.registrationName}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2'>
|
<div className="flex items-center gap-2">
|
||||||
<div className='text-description'>{t('receip.registration_number')}</div>
|
<div className="text-description">{t("receip.registration_number")}</div>
|
||||||
<div>{customer?.legalUser?.registrationCode}</div>
|
<div>{customer?.legalUser?.registrationCode}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-between items-center mt-7'>
|
<div className="flex justify-between items-center mt-7">
|
||||||
<div className='flex items-center gap-2 flex-1'>
|
<div className="flex items-center gap-2 flex-1">
|
||||||
<div className='text-description'>{t('receip.tel_company')}</div>
|
<div className="text-description">{t("receip.tel_company")}</div>
|
||||||
<div>{customer?.realUser?.phone || customer?.legalUser?.phone}</div>
|
<div>{customer?.realUser?.phone || customer?.legalUser?.phone}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2 flex-1'>
|
<div className="flex items-center gap-2 flex-1">
|
||||||
<div className='text-description'>{t('receip.national_code')}</div>
|
<div className="text-description">{t("receip.national_code")}</div>
|
||||||
<div>{customer.nationalCode}</div>
|
<div>{customer.nationalCode}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2 flex-1'>
|
<div className="flex items-center gap-2 flex-1">
|
||||||
<div className='text-description'>{t('receip.postal_code')}</div>
|
<div className="text-description">{t("receip.postal_code")}</div>
|
||||||
<div>{customer?.realUser?.address?.postalCode || customer?.legalUser?.address?.postalCode}</div>
|
<div>{customer?.realUser?.address?.postalCode || customer?.legalUser?.address?.postalCode}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-between items-center mt-7'>
|
<div className="flex justify-between items-center mt-7">
|
||||||
<div className='flex items-center gap-2 flex-1'>
|
<div className="flex items-center gap-2 flex-1">
|
||||||
<div className='text-description'>{t('receip.state')}</div>
|
<div className="text-description">{t("receip.state")}</div>
|
||||||
<div>{customer?.realUser?.address?.city?.province?.name || customer?.legalUser?.address?.city?.province?.name}</div>
|
<div>{customer?.realUser?.address?.city?.province?.name || customer?.legalUser?.address?.city?.province?.name}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2 flex-1'>
|
<div className="flex items-center gap-2 flex-1">
|
||||||
<div className='text-description'>{t('receip.city')}</div>
|
<div className="text-description">{t("receip.city")}</div>
|
||||||
<div>{customer?.realUser?.address?.city?.name || customer?.legalUser?.address?.city?.name}</div>
|
<div>{customer?.realUser?.address?.city?.name || customer?.legalUser?.address?.city?.name}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2 flex-1'>
|
<div className="flex items-center gap-2 flex-1">
|
||||||
<div className='text-description'>{t('receip.address')}</div>
|
<div className="text-description">{t("receip.address")}</div>
|
||||||
<div>{customer?.realUser?.address?.fullAddress || customer?.legalUser?.address?.fullAddress}</div>
|
<div>{customer?.realUser?.address?.fullAddress || customer?.legalUser?.address?.fullAddress}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
|
|
||||||
<div className='mt-10'>
|
<div className="mt-10">
|
||||||
<SwitchComponent
|
<SwitchComponent
|
||||||
label={t('receip.recurring')}
|
label={t("receip.recurring")}
|
||||||
active={isRecurring}
|
active={isRecurring}
|
||||||
onChange={(value) => setIsRecurring(value)}
|
onChange={(value) => setIsRecurring(value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{isRecurring && (
|
||||||
isRecurring &&
|
<div className="mt-10">
|
||||||
<div className='mt-10'>
|
<div>{t("receip.select_type")}</div>
|
||||||
<div>
|
|
||||||
{t('receip.select_type')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-4 rowTwoInput'>
|
<div className="mt-4 rowTwoInput">
|
||||||
<Select
|
<Select
|
||||||
label={t('select')}
|
label={t("select")}
|
||||||
items={[
|
items={[
|
||||||
{ label: t('receip.WEEKLY'), value: RecurringPeriodEnum.WEEKLY.toString() },
|
{ label: t("receip.WEEKLY"), value: RecurringPeriodEnum.WEEKLY.toString() },
|
||||||
{ label: t('receip.MONTHLY'), value: RecurringPeriodEnum.MONTHLY.toString() },
|
{ label: t("receip.MONTHLY"), value: RecurringPeriodEnum.MONTHLY.toString() },
|
||||||
{ label: t('receip.QUARTERLY'), value: RecurringPeriodEnum.QUARTERLY.toString() },
|
{ label: t("receip.QUARTERLY"), value: RecurringPeriodEnum.QUARTERLY.toString() },
|
||||||
{ label: t('receip.SEMIANNUALLY'), value: RecurringPeriodEnum.SEMIANNUALLY.toString() },
|
{ label: t("receip.SEMIANNUALLY"), value: RecurringPeriodEnum.SEMIANNUALLY.toString() },
|
||||||
{ label: t('receip.ANNUALLY'), value: RecurringPeriodEnum.ANNUALLY.toString() }
|
{ label: t("receip.ANNUALLY"), value: RecurringPeriodEnum.ANNUALLY.toString() },
|
||||||
]}
|
]}
|
||||||
value={type}
|
value={type}
|
||||||
onChange={(e) => setType(e.target.value as unknown as RecurringPeriodEnum)}
|
onChange={(e) => setType(e.target.value as unknown as RecurringPeriodEnum)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
label={t('receip.maxRecurringCycles')}
|
label={t("receip.maxRecurringCycles")}
|
||||||
name='maxRecurringCycles'
|
name="maxRecurringCycles"
|
||||||
value={maxRecurringCycles}
|
value={maxRecurringCycles}
|
||||||
onChange={(e) => setMaxRecurringCycles(+e.target.value)}
|
onChange={(e) => setMaxRecurringCycles(+e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
|
|
||||||
<div className='mt-10'>
|
<div className="mt-10">
|
||||||
<div>{t('receip.receipt_information')}</div>
|
<div>{t("receip.receipt_information")}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-8 border-b border-dashed border-border pb-7 flex items-end justify-between text-xs text-description'>
|
<div className="mt-8 border-b border-dashed border-border pb-7 flex items-end justify-between text-xs text-description">
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
<div>
|
<div>{t("receip.number")}</div>
|
||||||
{t('receip.number')}
|
<div className="size-10 bg-[#EBEDF5] rounded-xl flex justify-center items-center">{items.length + 1}</div>
|
||||||
</div>
|
|
||||||
<div className='size-10 bg-[#EBEDF5] rounded-xl flex justify-center items-center'>
|
|
||||||
{items.length + 1}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
<div>
|
<div>{t("receip.product_name")}</div>
|
||||||
{t('receip.product_name')}
|
|
||||||
</div>
|
|
||||||
<Input
|
<Input
|
||||||
className='text-center'
|
className="text-center"
|
||||||
name='name'
|
name="name"
|
||||||
value={formik.values.name}
|
value={formik.values.name}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
<div>
|
<div>{t("receip.count")}</div>
|
||||||
{t('receip.count')}
|
|
||||||
</div>
|
|
||||||
<Input
|
<Input
|
||||||
type='number'
|
type="number"
|
||||||
className='w-16 text-center'
|
className="w-16 text-center"
|
||||||
name='count'
|
name="count"
|
||||||
value={formik.values.count}
|
value={formik.values.count}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
<div>
|
<div>{t("receip.unit_amount")}</div>
|
||||||
{t('receip.unit_amount')}
|
|
||||||
</div>
|
|
||||||
<Input
|
<Input
|
||||||
className='text-center'
|
className="text-center"
|
||||||
name='unitPrice'
|
name="unitPrice"
|
||||||
value={formik.values.unitPrice}
|
value={formik.values.unitPrice}
|
||||||
onChange={(e) => formik.setFieldValue('unitPrice', e.target.value)}
|
onChange={(e) => formik.setFieldValue("unitPrice", e.target.value)}
|
||||||
seprator
|
seprator
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
<div>
|
<div>{t("receip.discount")}</div>
|
||||||
{t('receip.discount')}
|
|
||||||
</div>
|
|
||||||
<Input
|
<Input
|
||||||
type='number'
|
type="number"
|
||||||
className='w-16 text-center'
|
className="w-16 text-center"
|
||||||
name='discount'
|
name="discount"
|
||||||
value={formik.values.discount}
|
value={formik.values.discount}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
<div>
|
<div>{t("receip.total_amount")}</div>
|
||||||
{t('receip.total_amount')}
|
|
||||||
</div>
|
|
||||||
<Input
|
<Input
|
||||||
className='text-center bg-[#EBEDF5]'
|
className="text-center bg-[#EBEDF5]"
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div onClick={() => {
|
<div
|
||||||
|
onClick={() => {
|
||||||
if (formik.errors.name || formik.errors.count || formik.errors.unitPrice || formik.errors.discount) {
|
if (formik.errors.name || formik.errors.count || formik.errors.unitPrice || formik.errors.discount) {
|
||||||
toast(t('receip.error_empty'), 'error')
|
toast(t("receip.error_empty"), "error");
|
||||||
} else {
|
} else {
|
||||||
formik.handleSubmit()
|
formik.handleSubmit();
|
||||||
}
|
}
|
||||||
}} className='size-10 border border-border rounded-xl flex justify-center items-center'>
|
}}
|
||||||
<Add size={20} color='black' />
|
className="size-10 border border-border rounded-xl flex justify-center items-center"
|
||||||
|
>
|
||||||
|
<Add
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{items.map((item, index: number) => {
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
|
||||||
items.map((item, index: number) => {
|
|
||||||
return (
|
return (
|
||||||
<div key={item.name} className='mt-6 flex items-end justify-between text-xs text-description'>
|
<div
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
key={item.name}
|
||||||
{
|
className="mt-6 flex items-end justify-between text-xs text-description"
|
||||||
index === 0 &&
|
>
|
||||||
<div>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
{t('receip.number')}
|
{index === 0 && <div>{t("receip.number")}</div>}
|
||||||
|
<div className="size-10 bg-[#EBEDF5] rounded-xl flex justify-center items-center">{index + 1}</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
<div className="flex flex-col gap-2 items-center">
|
||||||
<div className='size-10 bg-[#EBEDF5] rounded-xl flex justify-center items-center'>
|
{index === 0 && <div>{t("receip.product_name")}</div>}
|
||||||
{index + 1}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
|
||||||
{
|
|
||||||
index === 0 &&
|
|
||||||
<div>
|
|
||||||
{t('receip.product_name')}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<Input
|
<Input
|
||||||
className='text-center'
|
className="text-center"
|
||||||
name='name'
|
name="name"
|
||||||
readOnly
|
readOnly
|
||||||
value={item.name}
|
value={item.name}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
{
|
{index === 0 && <div>{t("receip.count")}</div>}
|
||||||
index === 0 &&
|
|
||||||
<div>
|
|
||||||
{t('receip.count')}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<Input
|
<Input
|
||||||
type='number'
|
type="number"
|
||||||
className='w-16 text-center'
|
className="w-16 text-center"
|
||||||
name='count'
|
name="count"
|
||||||
readOnly
|
readOnly
|
||||||
value={item.count}
|
value={item.count}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
{
|
{index === 0 && <div>{t("receip.unit_amount")}</div>}
|
||||||
index === 0 &&
|
|
||||||
<div>
|
|
||||||
{t('receip.unit_amount')}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<Input
|
<Input
|
||||||
className='text-center'
|
className="text-center"
|
||||||
name='unitPrice'
|
name="unitPrice"
|
||||||
readOnly
|
readOnly
|
||||||
value={item.unitPrice}
|
value={item.unitPrice}
|
||||||
seprator
|
seprator
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
{
|
{index === 0 && <div>{t("receip.discount")}</div>}
|
||||||
index === 0 &&
|
|
||||||
<div>
|
|
||||||
{t('receip.discount')}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<Input
|
<Input
|
||||||
type='number'
|
type="number"
|
||||||
className='w-16 text-center'
|
className="w-16 text-center"
|
||||||
name='discount'
|
name="discount"
|
||||||
readOnly
|
readOnly
|
||||||
value={item.discount}
|
value={item.discount}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col gap-2 items-center'>
|
<div className="flex flex-col gap-2 items-center">
|
||||||
{
|
{index === 0 && <div>{t("receip.total_amount")}</div>}
|
||||||
index === 0 &&
|
|
||||||
<div>
|
|
||||||
{t('receip.total_amount')}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<Input
|
<Input
|
||||||
className='text-center bg-[#EBEDF5]'
|
className="text-center bg-[#EBEDF5]"
|
||||||
readOnly
|
readOnly
|
||||||
value={((Number(item.unitPrice) || 0) * (Number(item.count) || 0) - (Number(item.unitPrice) || 0) * (Number(item.count) || 0) * (Number(item.discount) / 100 || 0)).toFixed(1)}
|
value={((Number(item.unitPrice) || 0) * (Number(item.count) || 0) - (Number(item.unitPrice) || 0) * (Number(item.count) || 0) * (Number(item.discount) / 100 || 0)).toFixed(1)}
|
||||||
seprator
|
seprator
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div onClick={() => handleRemove(index)} className='size-10 border border-border rounded-xl flex justify-center items-center'>
|
<div
|
||||||
<Trash size={20} color='black' />
|
onClick={() => handleRemove(index)}
|
||||||
|
className="size-10 border border-border rounded-xl flex justify-center items-center"
|
||||||
|
>
|
||||||
|
<Trash
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
})
|
})}
|
||||||
}
|
|
||||||
|
|
||||||
|
<div className="mt-8 flex flex-col gap-4 items-end">
|
||||||
|
<div className="xl:w-1/2 h-12 w-full rounded-xl flex px-4 bg-[#EBEDF5] text-sm text-description justify-between items-center">
|
||||||
<div className='mt-8 flex flex-col gap-4 items-end'>
|
<div>{t("receip.tax")}</div>
|
||||||
<div className='xl:w-1/2 h-12 w-full rounded-xl flex px-4 bg-[#EBEDF5] text-sm text-description justify-between items-center'>
|
<div className="text-black">
|
||||||
<div>
|
|
||||||
{t('receip.tax')}
|
|
||||||
</div>
|
|
||||||
<div className='text-black'>
|
|
||||||
{(items.reduce((acc, item) => acc + (Number(item.unitPrice) || 0) * (Number(item.count) || 0) - (Number(item.unitPrice) || 0) * (Number(item.count) || 0) * (Number(item.discount) / 100 || 0), 0) * 0.10).toFixed(1)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='xl:w-1/2 h-12 w-full rounded-xl flex px-4 bg-[#EBEDF5] text-sm text-description justify-between items-center'>
|
|
||||||
<div>
|
|
||||||
{t('receip.total')}
|
|
||||||
</div>
|
|
||||||
<div className='text-black'>
|
|
||||||
{(
|
{(
|
||||||
items.reduce((acc, item) => acc + (Number(item.unitPrice) || 0) * (Number(item.count) || 0) - (Number(item.unitPrice) || 0) * (Number(item.count) || 0) * (Number(item.discount) / 100 || 0), 0) * 1.10
|
items.reduce(
|
||||||
|
(acc, item) => acc + (Number(item.unitPrice) || 0) * (Number(item.count) || 0) - (Number(item.unitPrice) || 0) * (Number(item.count) || 0) * (Number(item.discount) / 100 || 0),
|
||||||
|
0,
|
||||||
|
) * 0.1
|
||||||
|
).toFixed(1)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="xl:w-1/2 h-12 w-full rounded-xl flex px-4 bg-[#EBEDF5] text-sm text-description justify-between items-center">
|
||||||
|
<div>{t("receip.total")}</div>
|
||||||
|
<div className="text-black">
|
||||||
|
{(
|
||||||
|
items.reduce(
|
||||||
|
(acc, item) => acc + (Number(item.unitPrice) || 0) * (Number(item.count) || 0) - (Number(item.unitPrice) || 0) * (Number(item.count) || 0) * (Number(item.discount) / 100 || 0),
|
||||||
|
0,
|
||||||
|
) * 1.1
|
||||||
).toFixed(1)}
|
).toFixed(1)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='bg-white w-sidebar 3xl:block hidden py-10 px-5 h-fit rounded-3xl'>
|
<div className="bg-white w-sidebar 3xl:block hidden py-10 px-5 h-fit rounded-3xl">
|
||||||
<div className='text-sm'>
|
<div className="text-sm">{t("ticket.title_hint")}</div>
|
||||||
{t('ticket.title_hint')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-6'>
|
<div className="mt-6">
|
||||||
<div className='flex items-start gap-2 border-b pb-5'>
|
<div className="flex items-start gap-2 border-b pb-5">
|
||||||
<div>
|
<div>
|
||||||
<TickSquare size={20} color='black' variant='Bold' />
|
<TickSquare
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
variant="Bold"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-description text-xs leading-5'>
|
<div className="text-description text-xs leading-5">سوالات - مشکلاتی که به یک موضوع مربوط میشوند را در یک درخواست پشتیبانی پیگیر باشید و چند درخواست برای یک موضوع باز نکنید.</div>
|
||||||
سوالات - مشکلاتی که به یک موضوع مربوط میشوند را در یک درخواست پشتیبانی پیگیر باشید و چند درخواست برای یک موضوع باز نکنید.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex items-start gap-2 mt-6 border-b pb-5">
|
||||||
<div className='flex items-start gap-2 mt-6 border-b pb-5'>
|
|
||||||
<div>
|
<div>
|
||||||
<TickSquare size={20} color='black' variant='Bold' />
|
<TickSquare
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
variant="Bold"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-description text-xs leading-5'>
|
<div className="text-description text-xs leading-5">لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.</div>
|
||||||
لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex items-start gap-2 mt-6">
|
||||||
<div className='flex items-start gap-2 mt-6'>
|
|
||||||
<div>
|
<div>
|
||||||
<TickSquare size={20} color='black' variant='Bold' />
|
<TickSquare
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
variant="Bold"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-description text-xs leading-5'>
|
<div className="text-description text-xs leading-5">
|
||||||
پاسخگویی 24 ساعته تلفنی را تنها از میهن وب هاست می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
|
پاسخگویی 24 ساعته تلفنی را تنها از میهن وب هاست می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -554,9 +521,9 @@ const CreateReceipt: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default CreateReceipt
|
export default CreateReceipt;
|
||||||
|
|||||||
+140
-147
@@ -1,244 +1,237 @@
|
|||||||
import { FC, useState } from 'react'
|
import { useFormik } from "formik";
|
||||||
import { useTranslation } from 'react-i18next'
|
import { TickCircle, TickSquare } from "iconsax-react";
|
||||||
import Input from '../../components/Input'
|
import { FC, useState } from "react";
|
||||||
import Select from '../../components/Select'
|
import { useTranslation } from "react-i18next";
|
||||||
import Textarea from '../../components/Textarea'
|
import { useNavigate } from "react-router-dom";
|
||||||
import UploadBox from '../../components/UploadBox'
|
import * as Yup from "yup";
|
||||||
import Button from '../../components/Button'
|
import Button from "../../components/Button";
|
||||||
import { TickCircle, TickSquare } from 'iconsax-react'
|
import Input from "../../components/Input";
|
||||||
import { useCreateTicket, useGetCategories } from './hooks/useTicketData'
|
import PageLoading from "../../components/PageLoading";
|
||||||
import { useGetProfile } from '../profile/hooks/useProfileData'
|
import Select from "../../components/Select";
|
||||||
import PageLoading from '../../components/PageLoading'
|
import Textarea from "../../components/Textarea";
|
||||||
import { useFormik } from 'formik'
|
import { toast } from "../../components/Toast";
|
||||||
import * as Yup from 'yup'
|
import UploadBox from "../../components/UploadBox";
|
||||||
import { ErrorType } from '../../helpers/types'
|
import { Pages } from "../../config/Pages";
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { ErrorType } from "../../helpers/types";
|
||||||
import { Pages } from '../../config/Pages'
|
import { useGetCustomers } from "../customer/hooks/useCustomerData";
|
||||||
import { CreateTicketAdminType } from './types/TicketTypes'
|
import { CustomerItemType } from "../customer/types/CustomerTypes";
|
||||||
import { toast } from '../../components/Toast';
|
import { useGetProfile } from "../profile/hooks/useProfileData";
|
||||||
import { useMultiUpload } from '../service/hooks/useServiceData'
|
import { useMultiUpload } from "../service/hooks/useServiceData";
|
||||||
import { useGetCustomers } from '../customer/hooks/useCustomerData'
|
import { useCreateTicket, useGetCategories } from "./hooks/useTicketData";
|
||||||
import { CustomerItemType } from '../customer/types/CustomerTypes'
|
import { CreateTicketAdminType } from "./types/TicketTypes";
|
||||||
|
|
||||||
const CreateTicket: FC = () => {
|
const CreateTicket: FC = () => {
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation("global");
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate();
|
||||||
const [files, setFiles] = useState<File[]>([])
|
const [files, setFiles] = useState<File[]>([]);
|
||||||
const getProfile = useGetProfile()
|
const getProfile = useGetProfile();
|
||||||
const getCategories = useGetCategories()
|
const getCategories = useGetCategories();
|
||||||
const multiUpload = useMultiUpload()
|
const multiUpload = useMultiUpload();
|
||||||
const createTicket = useCreateTicket()
|
const createTicket = useCreateTicket();
|
||||||
const getCustomers = useGetCustomers(1, true)
|
const getCustomers = useGetCustomers(1, 50, true);
|
||||||
|
|
||||||
|
|
||||||
const formik = useFormik<CreateTicketAdminType>({
|
const formik = useFormik<CreateTicketAdminType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
subject: '',
|
subject: "",
|
||||||
message: '',
|
message: "",
|
||||||
categoryId: '',
|
categoryId: "",
|
||||||
priority: 'LOW',
|
priority: "LOW",
|
||||||
attachmentUrls: [],
|
attachmentUrls: [],
|
||||||
userId: ''
|
userId: "",
|
||||||
},
|
},
|
||||||
validationSchema: Yup.object({
|
validationSchema: Yup.object({
|
||||||
subject: Yup.string().required(t('errors.required')),
|
subject: Yup.string().required(t("errors.required")),
|
||||||
message: Yup.string().required(t('errors.required')),
|
message: Yup.string().required(t("errors.required")),
|
||||||
categoryId: Yup.string().required(t('errors.required')),
|
categoryId: Yup.string().required(t("errors.required")),
|
||||||
priority: Yup.string().required(t('errors.required')),
|
priority: Yup.string().required(t("errors.required")),
|
||||||
}),
|
}),
|
||||||
onSubmit: async (values) => {
|
onSubmit: async (values) => {
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
const images = new FormData()
|
const images = new FormData();
|
||||||
files.forEach(file => {
|
files.forEach((file) => {
|
||||||
images.append('files', file)
|
images.append("files", file);
|
||||||
})
|
});
|
||||||
await multiUpload.mutateAsync(images, {
|
await multiUpload.mutateAsync(images, {
|
||||||
onSuccess: async (data) => {
|
onSuccess: async (data) => {
|
||||||
values.attachmentUrls = await data.data.map((item: { url: string }) => item?.url)
|
values.attachmentUrls = await data.data.map((item: { url: string }) => item?.url);
|
||||||
},
|
},
|
||||||
onError: (error: ErrorType) => {
|
onError: (error: ErrorType) => {
|
||||||
toast(error.response?.data?.error?.message[0], 'error')
|
toast(error.response?.data?.error?.message[0], "error");
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
delete values.attachmentUrls
|
delete values.attachmentUrls;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.danakServiceId === '') {
|
if (values.danakServiceId === "") {
|
||||||
delete values.danakServiceId
|
delete values.danakServiceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
createTicket.mutate(values, {
|
createTicket.mutate(values, {
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
toast(t('success'), 'success')
|
toast(t("success"), "success");
|
||||||
formik.resetForm()
|
formik.resetForm();
|
||||||
navigate(Pages.ticket.detail + data.data?.ticketId)
|
navigate(Pages.ticket.detail + data.data?.ticketId);
|
||||||
},
|
},
|
||||||
onError: (error: ErrorType) => {
|
onError: (error: ErrorType) => {
|
||||||
toast(error.response?.data?.error?.message[0], 'error')
|
toast(error.response?.data?.error?.message[0], "error");
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-4 pb-12'>
|
<div className="mt-4 pb-12">
|
||||||
<div>
|
<div>{t("ticket.new_ticket")}</div>
|
||||||
{t('ticket.new_ticket')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
{getProfile.isPending || getCategories.isPending ? (
|
||||||
getProfile.isPending || getCategories.isPending ?
|
|
||||||
<PageLoading />
|
<PageLoading />
|
||||||
:
|
) : (
|
||||||
<div className='flex gap-6 xl:mt-8 mt-4'>
|
<div className="flex gap-6 xl:mt-8 mt-4">
|
||||||
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
<div className="flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl">
|
||||||
<div>
|
<div>{t("ticket.submit_your_message")}</div>
|
||||||
{t('ticket.submit_your_message')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-5'>
|
<div className="mt-5">
|
||||||
<Select
|
<Select
|
||||||
label={t('receip.customer')}
|
label={t("receip.customer")}
|
||||||
placeholder={t('select')}
|
placeholder={t("select")}
|
||||||
items={getCustomers.data?.data?.customers?.map((item: CustomerItemType) => {
|
items={getCustomers.data?.data?.customers?.map((item: CustomerItemType) => {
|
||||||
return {
|
return {
|
||||||
label: item.firstName + ' ' + item.lastName,
|
label: item.firstName + " " + item.lastName,
|
||||||
value: item.id
|
value: item.id,
|
||||||
}
|
};
|
||||||
})}
|
})}
|
||||||
{...formik.getFieldProps('userId')}
|
{...formik.getFieldProps("userId")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-6 rowTwoInput">
|
||||||
<div className='mt-6 rowTwoInput'>
|
|
||||||
<Input
|
<Input
|
||||||
label={t('ticket.subject')}
|
label={t("ticket.subject")}
|
||||||
placeholder={t('ticket.enter_your_subject')}
|
placeholder={t("ticket.enter_your_subject")}
|
||||||
name='subject'
|
name="subject"
|
||||||
value={formik.values.subject}
|
value={formik.values.subject}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
error_text={formik.touched.subject && formik.errors.subject ? formik.errors.subject : ''}
|
error_text={formik.touched.subject && formik.errors.subject ? formik.errors.subject : ""}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
label={t('ticket.priority')}
|
label={t("ticket.priority")}
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
label: t('ticket.low'),
|
label: t("ticket.low"),
|
||||||
value: 'LOW'
|
value: "LOW",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('ticket.medium'),
|
label: t("ticket.medium"),
|
||||||
value: 'MEDIUM'
|
value: "MEDIUM",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('ticket.high'),
|
label: t("ticket.high"),
|
||||||
value: 'HIGH'
|
value: "HIGH",
|
||||||
}
|
},
|
||||||
]}
|
]}
|
||||||
placeholder={t('select')}
|
placeholder={t("select")}
|
||||||
className='border'
|
className="border"
|
||||||
name='priority'
|
name="priority"
|
||||||
value={formik.values.priority}
|
value={formik.values.priority}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
error_text={formik.touched.priority && formik.errors.priority ? formik.errors.priority : ''}
|
error_text={formik.touched.priority && formik.errors.priority ? formik.errors.priority : ""}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-6 rowTwoInput'>
|
<div className="mt-6 rowTwoInput">
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
label={t('ticket.category')}
|
label={t("ticket.category")}
|
||||||
items={getCategories.data?.data?.ticketCategories?.map((item: { id: string, title: string }) => ({
|
items={getCategories.data?.data?.ticketCategories?.map((item: { id: string; title: string }) => ({
|
||||||
label: item.title,
|
label: item.title,
|
||||||
value: item.id
|
value: item.id,
|
||||||
})
|
}))}
|
||||||
)}
|
placeholder={t("select")}
|
||||||
placeholder={t('select')}
|
className="border"
|
||||||
className='border'
|
name="categoryId"
|
||||||
name='categoryId'
|
|
||||||
value={formik.values.categoryId}
|
value={formik.values.categoryId}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
error_text={formik.touched.categoryId && formik.errors.categoryId ? formik.errors.categoryId : ''}
|
error_text={formik.touched.categoryId && formik.errors.categoryId ? formik.errors.categoryId : ""}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-6'>
|
<div className="mt-6">
|
||||||
<Textarea
|
<Textarea
|
||||||
label={t('description')}
|
label={t("description")}
|
||||||
placeholder={t('ticket.your_description')}
|
placeholder={t("ticket.your_description")}
|
||||||
name='message'
|
name="message"
|
||||||
value={formik.values.message}
|
value={formik.values.message}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
error_text={formik.touched.message && formik.errors.message ? formik.errors.message : ''}
|
error_text={formik.touched.message && formik.errors.message ? formik.errors.message : ""}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-6'>
|
<div className="mt-6">
|
||||||
<UploadBox
|
<UploadBox
|
||||||
label={t('ticket.attachment')}
|
label={t("ticket.attachment")}
|
||||||
isMultiple
|
isMultiple
|
||||||
onChange={setFiles}
|
onChange={setFiles}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-8 flex justify-end'>
|
<div className="mt-8 flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
className='xl:max-w-[100px]'
|
className="xl:max-w-[100px]"
|
||||||
onClick={() => formik.handleSubmit()}
|
onClick={() => formik.handleSubmit()}
|
||||||
isLoading={createTicket.isPending || multiUpload.isPending}
|
isLoading={createTicket.isPending || multiUpload.isPending}
|
||||||
>
|
>
|
||||||
<div className='flex gap-2 items-center'>
|
<div className="flex gap-2 items-center">
|
||||||
<TickCircle size={20} color='white' />
|
<TickCircle
|
||||||
<div>
|
size={20}
|
||||||
{t('ticket.send')}
|
color="white"
|
||||||
</div>
|
/>
|
||||||
|
<div>{t("ticket.send")}</div>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='bg-white w-sidebar xl:block hidden py-10 px-5 h-fit rounded-3xl'>
|
<div className="bg-white w-sidebar xl:block hidden py-10 px-5 h-fit rounded-3xl">
|
||||||
<div className='text-sm'>
|
<div className="text-sm">{t("ticket.title_hint")}</div>
|
||||||
{t('ticket.title_hint')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-6'>
|
<div className="mt-6">
|
||||||
<div className='flex items-start gap-2 border-b pb-5'>
|
<div className="flex items-start gap-2 border-b pb-5">
|
||||||
<div>
|
<div>
|
||||||
<TickSquare size={20} color='black' variant='Bold' />
|
<TickSquare
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
variant="Bold"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-description text-xs leading-5'>
|
<div className="text-description text-xs leading-5">سوالات - مشکلاتی که به یک موضوع مربوط میشوند را در یک درخواست پشتیبانی پیگیر باشید و چند درخواست برای یک موضوع باز نکنید.</div>
|
||||||
سوالات - مشکلاتی که به یک موضوع مربوط میشوند را در یک درخواست پشتیبانی پیگیر باشید و چند درخواست برای یک موضوع باز نکنید.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex items-start gap-2 mt-6 border-b pb-5">
|
||||||
<div className='flex items-start gap-2 mt-6 border-b pb-5'>
|
|
||||||
<div>
|
<div>
|
||||||
<TickSquare size={20} color='black' variant='Bold' />
|
<TickSquare
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
variant="Bold"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-description text-xs leading-5'>
|
<div className="text-description text-xs leading-5">لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.</div>
|
||||||
لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex items-start gap-2 mt-6">
|
||||||
<div className='flex items-start gap-2 mt-6'>
|
|
||||||
<div>
|
<div>
|
||||||
<TickSquare size={20} color='black' variant='Bold' />
|
<TickSquare
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
variant="Bold"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-description text-xs leading-5'>
|
<div className="text-description text-xs leading-5">
|
||||||
پاسخگویی 24 ساعته تلفنی را تنها از داناک می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
|
پاسخگویی 24 ساعته تلفنی را تنها از داناک می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default CreateTicket
|
export default CreateTicket;
|
||||||
|
|||||||
Reference in New Issue
Block a user