@@ -1,4 +1,4 @@
|
|||||||
# VITE_API_BASE_URL = 'https://negareh-api.dev.danakcorp.com'
|
VITE_API_BASE_URL = 'http://localhost:4000'
|
||||||
VITE_API_BASE_URL = 'https://negare-api.danakcorp.com'
|
# VITE_API_BASE_URL = 'https://negare-api.danakcorp.com'
|
||||||
VITE_TOKEN_NAME = 'negareh_at'
|
VITE_TOKEN_NAME = 'negareh_at'
|
||||||
VITE_REFRESH_TOKEN_NAME = 'negareh_art'
|
VITE_REFRESH_TOKEN_NAME = 'negareh_art'
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ const Input: FC<Props> = (props: Props) => {
|
|||||||
<input {...props} onChange={(e) => {
|
<input {...props} onChange={(e) => {
|
||||||
setSearch(e.target.value)
|
setSearch(e.target.value)
|
||||||
handleInputChange(e)
|
handleInputChange(e)
|
||||||
}} value={props.seprator ? formattedValue : props.value} type={props.type === 'password' && showPassword ? 'text' : props.type === 'password' ? 'password' : undefined} className={inputClass} />
|
}} value={props.seprator ? formattedValue : props.value} type={props.type === 'password' ? (showPassword ? 'text' : 'password') : props.type} className={inputClass} />
|
||||||
|
|
||||||
{
|
{
|
||||||
props.type === 'password' &&
|
props.type === 'password' &&
|
||||||
|
|||||||
@@ -54,7 +54,9 @@ const LoginStep1: FC = () => {
|
|||||||
label={'شماره موبایل'}
|
label={'شماره موبایل'}
|
||||||
placeholder={'شماره موبایل خود را وارد کنید'}
|
placeholder={'شماره موبایل خود را وارد کنید'}
|
||||||
type='tel'
|
type='tel'
|
||||||
className='text-right'
|
autoComplete='tel'
|
||||||
|
inputMode='tel'
|
||||||
|
className='dltr text-left'
|
||||||
name='phone_email'
|
name='phone_email'
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
value={formik.values.phone_email}
|
value={formik.values.phone_email}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useRef, useState, type FC } from "react";
|
import { useEffect, useRef, useState, type FC } from "react";
|
||||||
import Button from "@/components/Button";
|
import Button from "@/components/Button";
|
||||||
import Select from "@/components/Select";
|
import Select from "@/components/Select";
|
||||||
import { TickSquare } from "iconsax-react";
|
import { Add, TickSquare } from "iconsax-react";
|
||||||
import { toast } from "@/shared/toast";
|
import { toast } from "@/shared/toast";
|
||||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
import { Paths } from "@/config/Paths";
|
import { Paths } from "@/config/Paths";
|
||||||
@@ -57,7 +57,6 @@ const CreateInvoice: FC = () => {
|
|||||||
userId: !requestId
|
userId: !requestId
|
||||||
? Yup.string().required("انتخاب مشتری اجباری است.")
|
? Yup.string().required("انتخاب مشتری اجباری است.")
|
||||||
: Yup.string(),
|
: Yup.string(),
|
||||||
approvalDeadline: Yup.string().required("مهلت تایید اجباری است."),
|
|
||||||
}),
|
}),
|
||||||
onSubmit: async (values) => {
|
onSubmit: async (values) => {
|
||||||
const validItems = items.filter(
|
const validItems = items.filter(
|
||||||
@@ -92,8 +91,12 @@ const CreateInvoice: FC = () => {
|
|||||||
description: i.description || "",
|
description: i.description || "",
|
||||||
})),
|
})),
|
||||||
enableTax: values.enableTax,
|
enableTax: values.enableTax,
|
||||||
approvalDeadline: moment(values.approvalDeadline, 'jYYYY/jMM/jDD').format('YYYY-MM-DD'),
|
...(values.approvalDeadline
|
||||||
...(values.paymentMethod ? { paymentMethod: values.paymentMethod } : {}),
|
? { approvalDeadline: moment(values.approvalDeadline, 'jYYYY/jMM/jDD').format('YYYY-MM-DD') }
|
||||||
|
: {}),
|
||||||
|
...(values.paymentMethod?.trim()
|
||||||
|
? { paymentMethod: values.paymentMethod.trim() }
|
||||||
|
: {}),
|
||||||
description: values.description,
|
description: values.description,
|
||||||
...(attachments.length ? { attachments } : {}),
|
...(attachments.length ? { attachments } : {}),
|
||||||
};
|
};
|
||||||
@@ -141,8 +144,8 @@ const CreateInvoice: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addItem = (currentIndex: number) => {
|
const addItem = () => {
|
||||||
const current = items[currentIndex];
|
const current = items[items.length - 1];
|
||||||
const hasProduct = !!current?.productId;
|
const hasProduct = !!current?.productId;
|
||||||
const hasQuantity = (current?.quantity ?? 0) > 0;
|
const hasQuantity = (current?.quantity ?? 0) > 0;
|
||||||
const hasUnitPrice = (current?.unitPrice ?? 0) > 0;
|
const hasUnitPrice = (current?.unitPrice ?? 0) > 0;
|
||||||
@@ -208,11 +211,21 @@ const CreateInvoice: FC = () => {
|
|||||||
item={item}
|
item={item}
|
||||||
index={index}
|
index={index}
|
||||||
onChange={handleItemChange}
|
onChange={handleItemChange}
|
||||||
onAdd={() => addItem(index)}
|
|
||||||
onRemove={() => removeItem(index)}
|
onRemove={() => removeItem(index)}
|
||||||
canRemove={items.length > 1}
|
canRemove={items.length > 1}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
<div className="mt-4 ">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={addItem}
|
||||||
|
className="h-10 px-4 rounded-xl bg-primary flex items-center gap-2 hover:opacity-90 transition-opacity"
|
||||||
|
aria-label="افزودن آیتم"
|
||||||
|
>
|
||||||
|
<Add size={18} color="black" />
|
||||||
|
<span className="text-[13px] font-light">افزودن آیتم</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6 flex gap-2 items-center">
|
<div className="mt-6 flex gap-2 items-center">
|
||||||
@@ -256,11 +269,6 @@ const CreateInvoice: FC = () => {
|
|||||||
value={formik.values.paymentMethod}
|
value={formik.values.paymentMethod}
|
||||||
onChange={(e) => formik.setFieldValue("paymentMethod", e.target.value)}
|
onChange={(e) => formik.setFieldValue("paymentMethod", e.target.value)}
|
||||||
onBlur={formik.handleBlur}
|
onBlur={formik.handleBlur}
|
||||||
error_text={
|
|
||||||
formik.errors.paymentMethod && formik.touched.paymentMethod
|
|
||||||
? formik.errors.paymentMethod
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,15 @@ const ProformaInvoice: FC = () => {
|
|||||||
const getServiceLabel = (item: InvoiceType) => {
|
const getServiceLabel = (item: InvoiceType) => {
|
||||||
if (!item.items?.length) return '-'
|
if (!item.items?.length) return '-'
|
||||||
if (item.items.length === 1) return item.items[0].product?.title ?? '-'
|
if (item.items.length === 1) return item.items[0].product?.title ?? '-'
|
||||||
return `${item.items.length} خدمت`
|
return (
|
||||||
|
<div className='flex flex-col gap-0.5'>
|
||||||
|
{item.items.map((line) => (
|
||||||
|
<span key={line.id}>
|
||||||
|
{line.product?.title ?? '-'} ({line.quantity.toLocaleString('fa-IR')})
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -129,6 +137,7 @@ const ProformaInvoice: FC = () => {
|
|||||||
{
|
{
|
||||||
key: 'service',
|
key: 'service',
|
||||||
title: 'خدمت',
|
title: 'خدمت',
|
||||||
|
className: '!whitespace-normal',
|
||||||
render: (item) => getServiceLabel(item),
|
render: (item) => getServiceLabel(item),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { useEffect, useState, type FC } from "react";
|
import { useEffect, useState, type FC } from "react";
|
||||||
import Button from "@/components/Button";
|
import Button from "@/components/Button";
|
||||||
import { TickSquare } from "iconsax-react";
|
import { Add, TickSquare } from "iconsax-react";
|
||||||
import { toast } from "@/shared/toast";
|
import { toast } from "@/shared/toast";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { Paths } from "@/config/Paths";
|
import { Paths } from "@/config/Paths";
|
||||||
import { extractErrorMessage } from "@/config/func";
|
import { extractErrorMessage } from "@/config/func";
|
||||||
import { useFormik } from "formik";
|
import { useFormik } from "formik";
|
||||||
import * as Yup from "yup";
|
|
||||||
import type { CreatePreInvoiceItemType, UpdatePreInvoiceType } from "./types/Types";
|
import type { CreatePreInvoiceItemType, UpdatePreInvoiceType } from "./types/Types";
|
||||||
import InvoiceItemRow from "./components/InvoiceItemRow";
|
import InvoiceItemRow from "./components/InvoiceItemRow";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
@@ -55,9 +54,6 @@ const UpdateInvoice: FC = () => {
|
|||||||
paymentMethod: "",
|
paymentMethod: "",
|
||||||
description: "",
|
description: "",
|
||||||
},
|
},
|
||||||
validationSchema: Yup.object({
|
|
||||||
approvalDeadline: Yup.string().required("مهلت تایید اجباری است."),
|
|
||||||
}),
|
|
||||||
onSubmit: async (values) => {
|
onSubmit: async (values) => {
|
||||||
const validItems = items.filter(
|
const validItems = items.filter(
|
||||||
(i) => i.productId && i.quantity > 0 && i.unitPrice >= 0
|
(i) => i.productId && i.quantity > 0 && i.unitPrice >= 0
|
||||||
@@ -96,11 +92,17 @@ const UpdateInvoice: FC = () => {
|
|||||||
description: i.description || "",
|
description: i.description || "",
|
||||||
})),
|
})),
|
||||||
enableTax: values.enableTax,
|
enableTax: values.enableTax,
|
||||||
|
...(values.approvalDeadline
|
||||||
|
? {
|
||||||
approvalDeadline: moment(
|
approvalDeadline: moment(
|
||||||
values.approvalDeadline,
|
values.approvalDeadline,
|
||||||
"jYYYY/jMM/jDD"
|
"jYYYY/jMM/jDD"
|
||||||
).format("YYYY-MM-DD"),
|
).format("YYYY-MM-DD"),
|
||||||
...(values.paymentMethod ? { paymentMethod: values.paymentMethod } : {}),
|
}
|
||||||
|
: {}),
|
||||||
|
...(values.paymentMethod?.trim()
|
||||||
|
? { paymentMethod: values.paymentMethod.trim() }
|
||||||
|
: {}),
|
||||||
description: values.description,
|
description: values.description,
|
||||||
...(attachments.length ? { attachments } : {}),
|
...(attachments.length ? { attachments } : {}),
|
||||||
};
|
};
|
||||||
@@ -164,8 +166,8 @@ const UpdateInvoice: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addItem = (currentIndex: number) => {
|
const addItem = () => {
|
||||||
const current = items[currentIndex];
|
const current = items[items.length - 1];
|
||||||
const hasProduct = !!current?.productId;
|
const hasProduct = !!current?.productId;
|
||||||
const hasQuantity = (current?.quantity ?? 0) > 0;
|
const hasQuantity = (current?.quantity ?? 0) > 0;
|
||||||
const hasUnitPrice = (current?.unitPrice ?? 0) > 0;
|
const hasUnitPrice = (current?.unitPrice ?? 0) > 0;
|
||||||
@@ -231,11 +233,21 @@ const UpdateInvoice: FC = () => {
|
|||||||
item={item}
|
item={item}
|
||||||
index={index}
|
index={index}
|
||||||
onChange={handleItemChange}
|
onChange={handleItemChange}
|
||||||
onAdd={() => addItem(index)}
|
|
||||||
onRemove={() => removeItem(index)}
|
onRemove={() => removeItem(index)}
|
||||||
canRemove={items.length > 1}
|
canRemove={items.length > 1}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
<div className="mt-4 flex justify-center">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={addItem}
|
||||||
|
className="h-10 px-4 rounded-xl bg-primary flex items-center gap-2 hover:opacity-90 transition-opacity"
|
||||||
|
aria-label="افزودن آیتم"
|
||||||
|
>
|
||||||
|
<Add size={18} color="black" />
|
||||||
|
<span className="text-[13px] font-light">افزودن آیتم</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6 flex gap-2 items-center">
|
<div className="mt-6 flex gap-2 items-center">
|
||||||
@@ -279,11 +291,6 @@ const UpdateInvoice: FC = () => {
|
|||||||
value={formik.values.paymentMethod}
|
value={formik.values.paymentMethod}
|
||||||
onChange={(e) => formik.setFieldValue("paymentMethod", e.target.value)}
|
onChange={(e) => formik.setFieldValue("paymentMethod", e.target.value)}
|
||||||
onBlur={formik.handleBlur}
|
onBlur={formik.handleBlur}
|
||||||
error_text={
|
|
||||||
formik.errors.paymentMethod && formik.touched.paymentMethod
|
|
||||||
? formik.errors.paymentMethod
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
getItemLineTotal,
|
getItemLineTotal,
|
||||||
getItemSubTotal,
|
getItemSubTotal,
|
||||||
} from "../utils/invoiceItem";
|
} from "../utils/invoiceItem";
|
||||||
import { Add, Trash } from "iconsax-react";
|
import { Trash } from "iconsax-react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { Paths } from "@/config/Paths";
|
import { Paths } from "@/config/Paths";
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -20,7 +20,6 @@ type Props = {
|
|||||||
field: keyof CreatePreInvoiceItemType,
|
field: keyof CreatePreInvoiceItemType,
|
||||||
value: string | number | null,
|
value: string | number | null,
|
||||||
) => void;
|
) => void;
|
||||||
onAdd: () => void;
|
|
||||||
onRemove: () => void;
|
onRemove: () => void;
|
||||||
canRemove: boolean;
|
canRemove: boolean;
|
||||||
onConvertToOrder?: () => void;
|
onConvertToOrder?: () => void;
|
||||||
@@ -31,7 +30,6 @@ const InvoiceItemRow: FC<Props> = ({
|
|||||||
item,
|
item,
|
||||||
index,
|
index,
|
||||||
onChange,
|
onChange,
|
||||||
onAdd,
|
|
||||||
onRemove,
|
onRemove,
|
||||||
canRemove,
|
canRemove,
|
||||||
onConvertToOrder,
|
onConvertToOrder,
|
||||||
@@ -170,14 +168,6 @@ const InvoiceItemRow: FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2 items-end h-10">
|
<div className="flex gap-2 items-end h-10">
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={onAdd}
|
|
||||||
className="w-10 h-10 rounded-full bg-primary flex items-center justify-center flex-shrink-0 hover:opacity-90 transition-opacity"
|
|
||||||
aria-label="افزودن آیتم"
|
|
||||||
>
|
|
||||||
<Add size={20} color="black" />
|
|
||||||
</button>
|
|
||||||
{canRemove && (
|
{canRemove && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export type CreatePreInvoiceType = {
|
|||||||
userId?: string;
|
userId?: string;
|
||||||
items: InvoiceItemSubmitType[];
|
items: InvoiceItemSubmitType[];
|
||||||
enableTax: boolean;
|
enableTax: boolean;
|
||||||
approvalDeadline: string;
|
approvalDeadline?: string;
|
||||||
paymentMethod?: string;
|
paymentMethod?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
attachments?: InvoiceAttachmentType[];
|
attachments?: InvoiceAttachmentType[];
|
||||||
@@ -38,7 +38,7 @@ export type CreatePreInvoiceType = {
|
|||||||
export type UpdatePreInvoiceType = {
|
export type UpdatePreInvoiceType = {
|
||||||
items: InvoiceItemSubmitType[];
|
items: InvoiceItemSubmitType[];
|
||||||
enableTax: boolean;
|
enableTax: boolean;
|
||||||
approvalDeadline: string;
|
approvalDeadline?: string;
|
||||||
paymentMethod?: string;
|
paymentMethod?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
attachments?: InvoiceAttachmentType[];
|
attachments?: InvoiceAttachmentType[];
|
||||||
@@ -113,7 +113,7 @@ export type InvoiceType = {
|
|||||||
enableTax: boolean;
|
enableTax: boolean;
|
||||||
approvalDeadline: string;
|
approvalDeadline: string;
|
||||||
attachments: InvoiceAttachmentType[];
|
attachments: InvoiceAttachmentType[];
|
||||||
paymentMethod: string;
|
paymentMethod?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
items: InvoiceItemType[];
|
items: InvoiceItemType[];
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user