design invoice
This commit is contained in:
@@ -6,6 +6,7 @@ import 'react-toastify/dist/ReactToastify.css'
|
|||||||
import { BrowserRouter } from 'react-router-dom'
|
import { BrowserRouter } from 'react-router-dom'
|
||||||
import { QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
import { QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||||
import { ToastContainer } from 'react-toastify'
|
import { ToastContainer } from 'react-toastify'
|
||||||
|
import Toast from '@/components/Toast'
|
||||||
import { getRefreshToken, getToken, removeRefreshToken, removeToken, setRefreshToken, setToken } from './config/func'
|
import { getRefreshToken, getToken, removeRefreshToken, removeToken, setRefreshToken, setToken } from './config/func'
|
||||||
import MainRouter from './router/MainRouter'
|
import MainRouter from './router/MainRouter'
|
||||||
import AuthRouter from './router/AuthRouter'
|
import AuthRouter from './router/AuthRouter'
|
||||||
@@ -131,6 +132,7 @@ const App: FC = () => {
|
|||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
{isLoggedIn ? <MainRouter /> : <AuthRouter />}
|
{isLoggedIn ? <MainRouter /> : <AuthRouter />}
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
<Toast />
|
||||||
<ToastContainer
|
<ToastContainer
|
||||||
position="top-left"
|
position="top-left"
|
||||||
autoClose={3000}
|
autoClose={3000}
|
||||||
|
|||||||
@@ -51,8 +51,9 @@ export const Paths = {
|
|||||||
home: '/home',
|
home: '/home',
|
||||||
myOrders: '/my-orders',
|
myOrders: '/my-orders',
|
||||||
perfomaInvoice: {
|
perfomaInvoice: {
|
||||||
list: '/perfoma-invoice/list',
|
list: '/invoice/list',
|
||||||
detail: '/perfoma-invoice/',
|
create: '/invoice/create',
|
||||||
|
detail: '/invoice/',
|
||||||
},
|
},
|
||||||
newOrder: '/new-order',
|
newOrder: '/new-order',
|
||||||
orderDetails: '/order/',
|
orderDetails: '/order/',
|
||||||
|
|||||||
@@ -0,0 +1,236 @@
|
|||||||
|
import { useState, useEffect, type FC } from "react";
|
||||||
|
import Button from "@/components/Button";
|
||||||
|
import Select from "@/components/Select";
|
||||||
|
import { TickSquare } from "iconsax-react";
|
||||||
|
import { toast } from "@/shared/toast";
|
||||||
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
|
import { Paths } from "@/config/Paths";
|
||||||
|
import { extractErrorMessage } from "@/config/func";
|
||||||
|
import { useFormik } from "formik";
|
||||||
|
import * as Yup from "yup";
|
||||||
|
import { useGetUsers } from "@/pages/user/hooks/useUserData";
|
||||||
|
import { useGetRequestDetail } from "@/pages/requests/hooks/useRequestData";
|
||||||
|
import { useCreatePreInvoice } from "./hooks/useInvoiceData";
|
||||||
|
import type { CreatePreInvoiceType, CreatePreInvoiceItemType } from "./types/Types";
|
||||||
|
import InvoiceItemRow from "./components/InvoiceItemRow";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import UploadBox from "@/components/UploadBox";
|
||||||
|
import Textarea from "@/components/Textarea";
|
||||||
|
|
||||||
|
const createEmptyItem = (): CreatePreInvoiceItemType => ({
|
||||||
|
productId: "",
|
||||||
|
quantity: 1,
|
||||||
|
unitPrice: 0,
|
||||||
|
discount: 0,
|
||||||
|
description: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const CreateInvoice: FC = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const requestId = searchParams.get("requestId") ?? undefined;
|
||||||
|
|
||||||
|
const { mutate: submitInvoice, isPending } = useCreatePreInvoice();
|
||||||
|
const { data: usersData } = useGetUsers();
|
||||||
|
const { data: requestData } = useGetRequestDetail(requestId || "");
|
||||||
|
|
||||||
|
const [items, setItems] = useState<CreatePreInvoiceItemType[]>([createEmptyItem()]);
|
||||||
|
const [, setAttachmentFiles] = useState<File[]>([]);
|
||||||
|
|
||||||
|
const formik = useFormik<Pick<CreatePreInvoiceType, "userId" | "enableTax" | "approvalDeadline" | "notes">>({
|
||||||
|
initialValues: {
|
||||||
|
userId: "",
|
||||||
|
enableTax: false,
|
||||||
|
approvalDeadline: "",
|
||||||
|
notes: "",
|
||||||
|
},
|
||||||
|
validationSchema: Yup.object({
|
||||||
|
userId: Yup.string().required("انتخاب مشتری اجباری است."),
|
||||||
|
approvalDeadline: Yup.string().required("نحوه پرداخت اجباری است."),
|
||||||
|
}),
|
||||||
|
onSubmit: (values) => {
|
||||||
|
const validItems = items.filter(
|
||||||
|
(i) => i.productId && i.quantity > 0 && i.unitPrice >= 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (validItems.length === 0) {
|
||||||
|
toast("حداقل یک آیتم با محصول، تعداد و مبلغ واحد معتبر اضافه کنید.", "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params: CreatePreInvoiceType = {
|
||||||
|
requestId,
|
||||||
|
userId: values.userId,
|
||||||
|
items: validItems.map((i) => ({
|
||||||
|
productId: i.productId,
|
||||||
|
quantity: i.quantity,
|
||||||
|
unitPrice: i.unitPrice,
|
||||||
|
discount: i.discount || 0,
|
||||||
|
description: i.description || "",
|
||||||
|
})),
|
||||||
|
enableTax: values.enableTax,
|
||||||
|
approvalDeadline: values.approvalDeadline,
|
||||||
|
notes: values.notes,
|
||||||
|
};
|
||||||
|
|
||||||
|
submitInvoice(params, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast("پیش فاکتور با موفقیت ثبت شد", "success");
|
||||||
|
navigate(Paths.perfomaInvoice.list);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast(extractErrorMessage(error), "error");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (requestId && requestData?.data?.user?.id) {
|
||||||
|
formik.setFieldValue("userId", String(requestData.data.user.id));
|
||||||
|
}
|
||||||
|
}, [requestId, requestData?.data?.user?.id]);
|
||||||
|
|
||||||
|
const handleItemChange = (
|
||||||
|
index: number,
|
||||||
|
field: keyof CreatePreInvoiceItemType,
|
||||||
|
value: string | number
|
||||||
|
) => {
|
||||||
|
const next = [...items];
|
||||||
|
next[index] = { ...next[index], [field]: value };
|
||||||
|
setItems(next);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addItem = (currentIndex: number) => {
|
||||||
|
const current = items[currentIndex];
|
||||||
|
const hasProduct = !!current?.productId;
|
||||||
|
const hasQuantity = (current?.quantity ?? 0) > 0;
|
||||||
|
const hasUnitPrice = (current?.unitPrice ?? 0) > 0;
|
||||||
|
|
||||||
|
if (!hasProduct || !hasQuantity || !hasUnitPrice) {
|
||||||
|
toast("لطفاً محصول، تعداد و مبلغ واحد را وارد کنید.", "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setItems([...items, createEmptyItem()]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeItem = (index: number) => {
|
||||||
|
if (items.length <= 1) return;
|
||||||
|
setItems(items.filter((_, i) => i !== index));
|
||||||
|
};
|
||||||
|
|
||||||
|
const totalAmount = items.reduce(
|
||||||
|
(sum, i) => sum + (i.quantity || 0) * (i.unitPrice || 0) - (i.discount || 0),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
const users = usersData?.data ?? [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-5">
|
||||||
|
<div className="justify-between items-center flex">
|
||||||
|
<h1 className="text-lg font-light">پیش فاکتور جدید</h1>
|
||||||
|
<Button
|
||||||
|
className="w-fit px-5"
|
||||||
|
onClick={() => formik.handleSubmit()}
|
||||||
|
isLoading={isPending}
|
||||||
|
>
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
<TickSquare size={20} color="black" />
|
||||||
|
<div>ثبت پیش فاکتور</div>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white rounded-3xl p-6 mt-8">
|
||||||
|
<div className="font-light">اطلاعات پیش فاکتور</div>
|
||||||
|
|
||||||
|
<div className="rowTwoInput mt-6">
|
||||||
|
<Select
|
||||||
|
items={users.map((u) => ({
|
||||||
|
label: u.firstName && u.lastName ? `${u.firstName} ${u.lastName}` : u.phone,
|
||||||
|
value: String(u.id),
|
||||||
|
}))}
|
||||||
|
label="مشتری"
|
||||||
|
placeholder="انتخاب"
|
||||||
|
value={formik.values.userId}
|
||||||
|
onChange={(e) => formik.setFieldValue("userId", e.target.value)}
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
error_text={
|
||||||
|
formik.errors.userId && formik.touched.userId ? formik.errors.userId : ""
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-6">
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<InvoiceItemRow
|
||||||
|
key={index}
|
||||||
|
item={item}
|
||||||
|
index={index}
|
||||||
|
onChange={handleItemChange}
|
||||||
|
onAdd={() => addItem(index)}
|
||||||
|
onRemove={() => removeItem(index)}
|
||||||
|
canRemove={items.length > 1}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-6 flex gap-2 items-center">
|
||||||
|
<Checkbox
|
||||||
|
id="enableTax"
|
||||||
|
checked={formik.values.enableTax}
|
||||||
|
onCheckedChange={(checked) =>
|
||||||
|
formik.setFieldValue("enableTax", !!checked)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="enableTax"
|
||||||
|
className="text-[13px] cursor-pointer select-none"
|
||||||
|
>
|
||||||
|
مالیات بر ارزش افزوده (۱۰ درصد)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-6">
|
||||||
|
<Textarea
|
||||||
|
label="نحوه پرداخت"
|
||||||
|
placeholder="مثال: مبلغ ۷۰ درصد پیش پرداخت / اعتبار ۲ روز"
|
||||||
|
value={formik.values.approvalDeadline}
|
||||||
|
onChange={(e) => formik.setFieldValue("approvalDeadline", e.target.value)}
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
error_text={
|
||||||
|
formik.errors.approvalDeadline && formik.touched.approvalDeadline
|
||||||
|
? formik.errors.approvalDeadline
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-6">
|
||||||
|
<Textarea
|
||||||
|
label="توضیحات"
|
||||||
|
placeholder="توضیحات پیش فاکتور را وارد کنید..."
|
||||||
|
value={formik.values.notes}
|
||||||
|
onChange={(e) => formik.setFieldValue("notes", e.target.value)}
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
name="notes"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-6">
|
||||||
|
<UploadBox label="فایل ضمیمه" onChange={setAttachmentFiles} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-6 bg-[#F5F7FC] h-12 px-4 flex justify-between items-center rounded-xl">
|
||||||
|
<div className="text-[#888888] text-[13px]">مبلغ کل:</div>
|
||||||
|
<div className="font-semibold text-[13px]">
|
||||||
|
{totalAmount.toLocaleString("fa-IR")} ریال
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CreateInvoice;
|
||||||
@@ -3,7 +3,7 @@ import { useMemo, useState, type FC } from 'react'
|
|||||||
import { ProformaInvoiceStatusEnum } from './enum/InvoiceEnum'
|
import { ProformaInvoiceStatusEnum } from './enum/InvoiceEnum'
|
||||||
import Filters from '@/components/Filters'
|
import Filters from '@/components/Filters'
|
||||||
import Table from '@/components/Table'
|
import Table from '@/components/Table'
|
||||||
import { Eye } from 'iconsax-react'
|
import { Eye, Add } 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'
|
||||||
import { useGetOrdersInvoiced } from '../order/hooks/useOrderData'
|
import { useGetOrdersInvoiced } from '../order/hooks/useOrderData'
|
||||||
@@ -79,7 +79,15 @@ const ProformaInvoice: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-5'>
|
<div className='mt-5'>
|
||||||
|
<div className='flex justify-between items-center'>
|
||||||
<h1 className='text-lg font-light'>پیش فاکتورها</h1>
|
<h1 className='text-lg font-light'>پیش فاکتورها</h1>
|
||||||
|
<Link to={Paths.perfomaInvoice.create}>
|
||||||
|
<button className='flex items-center gap-2 px-4 py-2 bg-primary text-black text-sm font-medium rounded-xl hover:opacity-90 transition-opacity'>
|
||||||
|
<Add size={18} />
|
||||||
|
پیش فاکتور جدید
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className='mt-8'>
|
<div className='mt-8'>
|
||||||
<Tabs
|
<Tabs
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import { type FC, type ChangeEvent } from "react";
|
||||||
|
import Input from "@/components/Input";
|
||||||
|
import ProductsSelect from "@/pages/order/components/ProductsSelect";
|
||||||
|
import type { CreatePreInvoiceItemType } from "../types/Types";
|
||||||
|
import { Add, Trash } from "iconsax-react";
|
||||||
|
type Props = {
|
||||||
|
item: CreatePreInvoiceItemType;
|
||||||
|
index: number;
|
||||||
|
onChange: (index: number, field: keyof CreatePreInvoiceItemType, value: string | number) => void;
|
||||||
|
onAdd: () => void;
|
||||||
|
onRemove: () => void;
|
||||||
|
canRemove: boolean;
|
||||||
|
error?: Partial<Record<keyof CreatePreInvoiceItemType, string>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const InvoiceItemRow: FC<Props> = ({
|
||||||
|
item,
|
||||||
|
index,
|
||||||
|
onChange,
|
||||||
|
onAdd,
|
||||||
|
onRemove,
|
||||||
|
canRemove,
|
||||||
|
error,
|
||||||
|
}) => {
|
||||||
|
const total = (item.quantity || 0) * (item.unitPrice || 0) - (item.discount || 0);
|
||||||
|
|
||||||
|
const handleProductChange = (e: ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
onChange(index, "productId", e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-wrap items-end gap-4 mt-6">
|
||||||
|
<div className="min-w-[140px] flex-1">
|
||||||
|
<ProductsSelect
|
||||||
|
value={item.productId}
|
||||||
|
onChange={handleProductChange}
|
||||||
|
error_text={error?.productId}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="min-w-[140px] flex-1">
|
||||||
|
<Input
|
||||||
|
label="توضیحات"
|
||||||
|
value={item.description}
|
||||||
|
onChange={(e) => onChange(index, "description", e.target.value)}
|
||||||
|
error_text={error?.description}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-[100px]">
|
||||||
|
<Input
|
||||||
|
label="تعداد"
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
value={item.quantity || ""}
|
||||||
|
onChange={(e) =>
|
||||||
|
onChange(index, "quantity", e.target.value ? Number(e.target.value) : 0)
|
||||||
|
}
|
||||||
|
error_text={error?.quantity}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-[120px]">
|
||||||
|
<Input
|
||||||
|
label="مبلغ واحد"
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
value={item.unitPrice ? String(item.unitPrice) : ""}
|
||||||
|
seprator
|
||||||
|
onChange={(e) => {
|
||||||
|
const raw = String(e.target.value).replace(/,/g, "");
|
||||||
|
onChange(index, "unitPrice", raw ? Number(raw) : 0);
|
||||||
|
}}
|
||||||
|
error_text={error?.unitPrice}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-[120px]">
|
||||||
|
<Input
|
||||||
|
label="تخفیف"
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
value={item.discount ? String(item.discount) : ""}
|
||||||
|
seprator
|
||||||
|
onChange={(e) => {
|
||||||
|
const raw = String(e.target.value).replace(/,/g, "");
|
||||||
|
onChange(index, "discount", raw ? Number(raw) : 0);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-[120px]">
|
||||||
|
<Input
|
||||||
|
label="مبلغ کل"
|
||||||
|
readOnly
|
||||||
|
value={total >= 0 ? total.toLocaleString("fa-IR") : "۰"}
|
||||||
|
unit="ریال"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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 && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onRemove}
|
||||||
|
className="w-10 h-10 rounded-full border border-border flex items-center justify-center flex-shrink-0 hover:bg-red-50 hover:border-red-200 transition-colors"
|
||||||
|
aria-label="حذف آیتم"
|
||||||
|
>
|
||||||
|
<Trash size={18} color="#dc2626" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InvoiceItemRow;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import * as api from "../service/InvoiceService";
|
||||||
|
import type { CreatePreInvoiceType } from "../types/Types";
|
||||||
|
|
||||||
|
export const useCreatePreInvoice = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (params: CreatePreInvoiceType) => api.createPreInvoice(params),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["orders-invoiced"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import axios from "@/config/axios";
|
||||||
|
import type { CreatePreInvoiceType } from "../types/Types";
|
||||||
|
|
||||||
|
export const createPreInvoice = async (params: CreatePreInvoiceType) => {
|
||||||
|
const { data } = await axios.post("/admin/pre-invoice", params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
export type CreatePreInvoiceItemType = {
|
||||||
|
productId: string;
|
||||||
|
quantity: number;
|
||||||
|
unitPrice: number;
|
||||||
|
discount: number;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CreatePreInvoiceType = {
|
||||||
|
requestId?: string;
|
||||||
|
userId: string;
|
||||||
|
items: CreatePreInvoiceItemType[];
|
||||||
|
enableTax: boolean;
|
||||||
|
approvalDeadline: string;
|
||||||
|
notes?: string;
|
||||||
|
};
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
import { type FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { useGetRequestDetail } from './hooks/useRequestData'
|
import { useGetRequestDetail } from './hooks/useRequestData'
|
||||||
import { useParams } from 'react-router-dom'
|
import { Link, useParams } from 'react-router-dom'
|
||||||
import RequestItem from './components/RequestItem'
|
import RequestItem from './components/RequestItem'
|
||||||
|
import { Paths } from '@/config/Paths'
|
||||||
|
import Button from '@/components/Button'
|
||||||
|
import { TickSquare } from 'iconsax-react'
|
||||||
|
|
||||||
const RequestDetail: FC = () => {
|
const RequestDetail: FC = () => {
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
@@ -14,6 +17,14 @@ const RequestDetail: FC = () => {
|
|||||||
<div className="text-sm text-[#8C90A3]">
|
<div className="text-sm text-[#8C90A3]">
|
||||||
درخواست #{data?.data?.requestNumber}
|
درخواست #{data?.data?.requestNumber}
|
||||||
</div>
|
</div>
|
||||||
|
<Link to={Paths.perfomaInvoice.create + `?requestId=${id}`}>
|
||||||
|
<Button className="w-fit px-5">
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
<TickSquare size={20} color="black" />
|
||||||
|
<div>ثبت پیش فاکتور</div>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Request Information Section */}
|
{/* Request Information Section */}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import SideBar from "../shared/Sidebar";
|
|||||||
import Header from "../shared/Header";
|
import Header from "../shared/Header";
|
||||||
import { Paths } from "@/config/Paths";
|
import { Paths } from "@/config/Paths";
|
||||||
import ProformaInvoice from "@/pages/invoice/ProformaInvoice";
|
import ProformaInvoice from "@/pages/invoice/ProformaInvoice";
|
||||||
|
import CreateInvoice from "@/pages/invoice/Create";
|
||||||
import RequestList from "@/pages/requests/RequestList";
|
import RequestList from "@/pages/requests/RequestList";
|
||||||
import ProductList from "@/pages/product/List";
|
import ProductList from "@/pages/product/List";
|
||||||
import OrdersList from "@/pages/order/List";
|
import OrdersList from "@/pages/order/List";
|
||||||
@@ -60,6 +61,7 @@ const MainRouter: FC = () => {
|
|||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
<Route path={Paths.home} element={<Home />} />
|
<Route path={Paths.home} element={<Home />} />
|
||||||
<Route path={Paths.perfomaInvoice.list} element={<ProformaInvoice />} />
|
<Route path={Paths.perfomaInvoice.list} element={<ProformaInvoice />} />
|
||||||
|
<Route path={Paths.perfomaInvoice.create} element={<CreateInvoice />} />
|
||||||
<Route path={Paths.perfomaInvoice.detail + ":id"} element={<DetailPerfomaInvoice />} />
|
<Route path={Paths.perfomaInvoice.detail + ":id"} element={<DetailPerfomaInvoice />} />
|
||||||
<Route path={Paths.requests.list} element={<RequestList />} />
|
<Route path={Paths.requests.list} element={<RequestList />} />
|
||||||
<Route path={Paths.requests.detail + ":id"} element={<RequestDetail />} />
|
<Route path={Paths.requests.detail + ":id"} element={<RequestDetail />} />
|
||||||
|
|||||||
Reference in New Issue
Block a user