fixed file uploading and payee
This commit is contained in:
parent
611116b70c
commit
0dc68eb20d
@ -307,7 +307,7 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
|
||||
})
|
||||
}
|
||||
></i>
|
||||
{canDetetExpense(expense) &&
|
||||
{
|
||||
canEditExpense(expense) && (
|
||||
<div className="dropdown z-2">
|
||||
<button
|
||||
|
||||
@ -13,7 +13,7 @@ export const ExpenseSchema = (expenseTypes) => {
|
||||
return z
|
||||
.object({
|
||||
projectId: z.string().min(1, { message: "Project is required" }),
|
||||
expensesCategoryId: z
|
||||
expenseCategoryId: z
|
||||
.string()
|
||||
.min(1, { message: "Expense type is required" }),
|
||||
paymentModeId: z.string().min(1, { message: "Payment mode is required" }),
|
||||
|
||||
@ -12,7 +12,7 @@ const Filelist = ({ files, removeFile, expenseToEdit }) => {
|
||||
return true;
|
||||
})
|
||||
.map((file, idx) => (
|
||||
<div className="col-12 col-sm-6 col-md-4 col-lg-8 bg-white shadow-sm rounded p-2 m-2">
|
||||
<div className="col-12 col-sm-6 col-md-4 col-lg-8 bg-white shadow-sm rounded p-2 m-2" key={idx}>
|
||||
<div className="row align-items-center">
|
||||
{/* File icon and info */}
|
||||
<div className="col-10 d-flex align-items-center gap-2">
|
||||
|
||||
@ -31,6 +31,7 @@ import Label from "../common/Label";
|
||||
import EmployeeSearchInput from "../common/EmployeeSearchInput";
|
||||
import Filelist from "./Filelist";
|
||||
|
||||
|
||||
const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
||||
const {
|
||||
data,
|
||||
@ -148,7 +149,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
||||
if (expenseToEdit && data) {
|
||||
reset({
|
||||
projectId: data.project.id || "",
|
||||
expensesCategoryId: data.expensesType.id || "",
|
||||
expenseCategoryId: data.expensesCategory?.id || "",
|
||||
paymentModeId: data.paymentMode.id || "",
|
||||
paidById: data.paidBy.id || "",
|
||||
transactionDate: data.transactionDate?.slice(0, 10) || "",
|
||||
@ -245,7 +246,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
||||
<select
|
||||
className="form-select form-select-sm"
|
||||
id="expensesCategoryId"
|
||||
{...register("expensesCategoryId")}
|
||||
{...register("expenseCategoryId")}
|
||||
>
|
||||
<option value="" disabled>
|
||||
Select Type
|
||||
@ -480,7 +481,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row my-2 text-start">
|
||||
<div className="row my-2 text-start">
|
||||
<div className="col-md-12">
|
||||
<Label className="form-label" required>
|
||||
Upload Bill{" "}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -7,56 +7,48 @@ const ALLOWED_TYPES = [
|
||||
"image/jpg",
|
||||
"image/jpeg",
|
||||
];
|
||||
export const PaymentRequestSchema = (expenseTypes,isItself) => {
|
||||
return z
|
||||
.object({
|
||||
title: z.string().min(1, { message: "Project is required" }),
|
||||
projectId: z.string().min(1, { message: "Project is required" }),
|
||||
expenseCategoryId: z
|
||||
.string()
|
||||
.min(1, { message: "Expense Category is required" }),
|
||||
currencyId: z
|
||||
.string()
|
||||
.min(1, { message: "Currency is required" }),
|
||||
dueDate: z.string().min(1, { message: "Date is required" }),
|
||||
description: z.string().min(1, { message: "Description is required" }),
|
||||
payee: z.string().min(1, { message: "Supplier name is required" }),
|
||||
isAdvancePayment: z.boolean().optional(),
|
||||
amount: z.coerce
|
||||
.number({
|
||||
invalid_type_error: "Amount is required and must be a number",
|
||||
export const PaymentRequestSchema = (expenseTypes, isItself) => {
|
||||
return z.object({
|
||||
title: z.string().min(1, { message: "Project is required" }),
|
||||
projectId: z.string().min(1, { message: "Project is required" }),
|
||||
expenseCategoryId: z
|
||||
.string()
|
||||
.min(1, { message: "Expense Category is required" }),
|
||||
currencyId: z.string().min(1, { message: "Currency is required" }),
|
||||
dueDate: z.string().min(1, { message: "Date is required" }),
|
||||
description: z.string().min(1, { message: "Description is required" }),
|
||||
payee: z.string().min(1, { message: "Supplier name is required" }),
|
||||
isAdvancePayment: z.boolean().optional(),
|
||||
amount: z.coerce
|
||||
.number({
|
||||
invalid_type_error: "Amount is required and must be a number",
|
||||
})
|
||||
.min(1, "Amount must be Enter")
|
||||
.refine((val) => /^\d+(\.\d{1,2})?$/.test(val.toString()), {
|
||||
message: "Amount must have at most 2 decimal places",
|
||||
}),
|
||||
billAttachments: z
|
||||
.array(
|
||||
z.object({
|
||||
fileName: z.string().min(1, { message: "Filename is required" }),
|
||||
base64Data: z.string().nullable(),
|
||||
contentType: z.string().refine((val) => ALLOWED_TYPES.includes(val), {
|
||||
message: "Only PDF, PNG, JPG, or JPEG files are allowed",
|
||||
}),
|
||||
documentId: z.string().optional(),
|
||||
fileSize: z.number().max(MAX_FILE_SIZE, {
|
||||
message: "File size must be less than or equal to 5MB",
|
||||
}),
|
||||
description: z.string().optional(),
|
||||
isActive: z.boolean().default(true),
|
||||
})
|
||||
.min(1, "Amount must be Enter")
|
||||
.refine((val) => /^\d+(\.\d{1,2})?$/.test(val.toString()), {
|
||||
message: "Amount must have at most 2 decimal places",
|
||||
}),
|
||||
billAttachments: z
|
||||
.array(
|
||||
z.object({
|
||||
fileName: z.string().min(1, { message: "Filename is required" }),
|
||||
base64Data: z.string().nullable(),
|
||||
contentType: z
|
||||
.string()
|
||||
.refine((val) => ALLOWED_TYPES.includes(val), {
|
||||
message: "Only PDF, PNG, JPG, or JPEG files are allowed",
|
||||
}),
|
||||
documentId: z.string().optional(),
|
||||
fileSize: z.number().max(MAX_FILE_SIZE, {
|
||||
message: "File size must be less than or equal to 5MB",
|
||||
}),
|
||||
description: z.string().optional(),
|
||||
isActive: z.boolean().default(true),
|
||||
})
|
||||
).refine((data)=>{
|
||||
if(isItself){
|
||||
payee.z.string().optional();
|
||||
}
|
||||
}),
|
||||
})
|
||||
};
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
};
|
||||
|
||||
export const defaultPaymentRequest = {
|
||||
title:"",
|
||||
title: "",
|
||||
description: "",
|
||||
payee: "",
|
||||
currencyId: "",
|
||||
@ -64,11 +56,10 @@ export const defaultPaymentRequest = {
|
||||
dueDate: "",
|
||||
projectId: "",
|
||||
expenseCategoryId: "",
|
||||
isAdvancePayment:boolean,
|
||||
isAdvancePayment: boolean,
|
||||
billAttachments: [],
|
||||
};
|
||||
|
||||
|
||||
export const SearchPaymentRequestSchema = z.object({
|
||||
projectIds: z.array(z.string()).optional(),
|
||||
statusIds: z.array(z.string()).optional(),
|
||||
@ -91,7 +82,6 @@ export const defaultPaymentRequestFilter = {
|
||||
endDate: null,
|
||||
};
|
||||
|
||||
|
||||
export const PaymentRequestActionScheam = (
|
||||
isTransaction = false,
|
||||
transactionDate
|
||||
@ -103,14 +93,13 @@ export const PaymentRequestActionScheam = (
|
||||
paymentRequestId: z.string().nullable().optional(),
|
||||
paidAt: z.string().nullable().optional(),
|
||||
paidById: z.string().nullable().optional(),
|
||||
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
if (isTransaction) {
|
||||
if (!data.paymentRequestId?.trim()) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ["reimburseTransactionId"],
|
||||
path: ["paymentRequestId"],
|
||||
message: "Reimburse Transaction ID is required",
|
||||
});
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ const ViewPaymentRequest = ({ requestId }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="container px-3" onSubmit={handleSubmit(onSubmit)}>
|
||||
<form className="container-xl px-3" onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="col-12 mb-1">
|
||||
<h5 className="fw-semibold m-0">Payment Request Details</h5>
|
||||
<hr />
|
||||
@ -325,14 +325,14 @@ const ViewPaymentRequest = ({ requestId }) => {
|
||||
<label className="fw-semibold form-label">Description : </label>
|
||||
<div className="text-muted">{data?.description}</div>
|
||||
</div>
|
||||
<div className="col-6 text-start">
|
||||
<div className="col-12 text-start">
|
||||
<label className="form-label me-2 mb-2 fw-semibold">
|
||||
Attachment :
|
||||
</label>
|
||||
|
||||
<div className="d-flex flex-wrap gap-2">
|
||||
{data?.documents?.length > 0 ? (
|
||||
data?.documents?.map((doc) => {
|
||||
{data?.attachments?.length > 0 ? (
|
||||
data?.attachments?.map((doc) => {
|
||||
const isImage = doc?.contentType?.includes("image");
|
||||
|
||||
return (
|
||||
|
||||
@ -54,7 +54,7 @@ const Timeline = ({ items = [], transparent = true }) => {
|
||||
<div className="d-flex flex-wrap align-items-center mb-2">
|
||||
<ul className="list-unstyled users-list d-flex align-items-center avatar-group m-0">
|
||||
{item.users.map((user, i) => (
|
||||
<li key={i} className="avatar me-1" title={user.name}>
|
||||
<li key={i} className="m-0" title={user.name}>
|
||||
{user.avatar ? (
|
||||
<img
|
||||
src={user.avatar}
|
||||
@ -64,7 +64,7 @@ const Timeline = ({ items = [], transparent = true }) => {
|
||||
height="32"
|
||||
/>
|
||||
) : (
|
||||
<Avatar
|
||||
<Avatar size="xs"
|
||||
firstName={user.firstName}
|
||||
lastName={user.lastName}
|
||||
/>
|
||||
@ -75,7 +75,7 @@ const Timeline = ({ items = [], transparent = true }) => {
|
||||
|
||||
{item.users?.length === 1 && (
|
||||
<div className="m-0">
|
||||
<p className="mb-0 small fw-medium">{`${item.users[0].firstName} ${item.users[0].lastName}`}</p>
|
||||
<p className="mb-0 text-xs fw-medium">{`${item.users[0].firstName} ${item.users[0].lastName}`}</p>
|
||||
<small>{item.users[0].role}</small>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -5,6 +5,19 @@ import { queryClient } from "../layouts/AuthLayout";
|
||||
import { useSelector } from "react-redux";
|
||||
import moment from "moment";
|
||||
|
||||
|
||||
|
||||
export const usePayee =()=>{
|
||||
return useQuery({
|
||||
queryKey:["payee"],
|
||||
queryFn:async()=>{
|
||||
const resp = await ExpenseRepository.GetPayee();
|
||||
return resp.data;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// -------------------Query------------------------------------------------------
|
||||
|
||||
const cleanFilter = (filter) => {
|
||||
|
||||
@ -8,6 +8,7 @@ import PaymentRequestList from "../../components/PaymentRequest/PaymentRequestLi
|
||||
import PaymentRequestFilterPanel from "../../components/PaymentRequest/PaymentRequestFilterPanel";
|
||||
import { defaultPaymentRequestFilter,SearchPaymentRequestSchema } from "../../components/PaymentRequest/PaymentRequestSchema";
|
||||
import ViewPaymentRequest from "../../components/PaymentRequest/ViewPaymentRequest";
|
||||
import PreviewDocument from "../../components/Expenses/PreviewDocument";
|
||||
|
||||
export const PaymentRequestContext = createContext();
|
||||
export const usePaymentRequestContext = () => {
|
||||
@ -25,12 +26,16 @@ const PaymentRequestPage = () => {
|
||||
const [ViewRequest,setVieRequest] = useState({view:false,requestId:null})
|
||||
const { setOffcanvasContent, setShowTrigger } = useFab();
|
||||
const [filters, setFilters] = useState(defaultPaymentRequestFilter);
|
||||
|
||||
const [ViewDocument, setDocumentView] = useState({
|
||||
IsOpen: false,
|
||||
Image: null,
|
||||
});
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
const contextValue = {
|
||||
setManageRequest,
|
||||
setVieRequest
|
||||
setVieRequest,
|
||||
setDocumentView
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@ -120,7 +125,7 @@ const PaymentRequestPage = () => {
|
||||
{ViewRequest.view && (
|
||||
<GlobalModel
|
||||
isOpen
|
||||
size="xl"
|
||||
|
||||
modalType="top"
|
||||
closeModal={() => setVieRequest({ requestId: null, view: false })}
|
||||
>
|
||||
@ -128,6 +133,17 @@ const PaymentRequestPage = () => {
|
||||
</GlobalModel>
|
||||
)}
|
||||
|
||||
{ViewDocument.IsOpen && (
|
||||
<GlobalModel
|
||||
isOpen
|
||||
size="md"
|
||||
key={ViewDocument.Image ?? "doc"}
|
||||
closeModal={() => setDocumentView({ IsOpen: false, Image: null })}
|
||||
>
|
||||
<PreviewDocument imageUrl={ViewDocument.Image} />
|
||||
</GlobalModel>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</PaymentRequestContext.Provider>
|
||||
);
|
||||
|
||||
@ -1,37 +1,50 @@
|
||||
import { api } from "../utils/axiosClient";
|
||||
|
||||
|
||||
const ExpenseRepository = {
|
||||
GetPayee: () => api.get("/api/Expense/payment-request/payee"),
|
||||
//#region Expense
|
||||
GetExpenseList: (pageSize, pageNumber, filter, searchString) => {
|
||||
const payloadJsonString = JSON.stringify(filter);
|
||||
return api.get(`/api/expense/list?pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${payloadJsonString}&searchString=${searchString}`);
|
||||
return api.get(
|
||||
`/api/expense/list?pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${payloadJsonString}&searchString=${searchString}`
|
||||
);
|
||||
},
|
||||
GetExpenseDetails: (id) => api.get(`/api/Expense/details/${id}`),
|
||||
CreateExpense: (data) => api.post("/api/Expense/create", data),
|
||||
UpdateExpense: (id, data) => api.put(`/api/Expense/edit/${id}`, data),
|
||||
DeleteExpense: (id) => api.delete(`/api/Expense/delete/${id}`),
|
||||
ActionOnExpense: (data) => api.post('/api/expense/action', data),
|
||||
GetExpenseFilter: () => api.get('/api/Expense/filter'),
|
||||
ActionOnExpense: (data) => api.post("/api/expense/action", data),
|
||||
GetExpenseFilter: () => api.get("/api/Expense/filter"),
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Payment Request
|
||||
GetPaymentRequestList: (pageSize, pageNumber, filter, isActive, searchString) => {
|
||||
GetPaymentRequestList: (
|
||||
pageSize,
|
||||
pageNumber,
|
||||
filter,
|
||||
isActive,
|
||||
searchString
|
||||
) => {
|
||||
const payloadJsonString = JSON.stringify(filter);
|
||||
return api.get(`/api/Expense/get/payment-requests/list?isActive=${isActive}&pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${payloadJsonString}&searchString=${searchString}`);
|
||||
return api.get(
|
||||
`/api/Expense/get/payment-requests/list?isActive=${isActive}&pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${payloadJsonString}&searchString=${searchString}`
|
||||
);
|
||||
},
|
||||
CreatePaymentRequest: (data) => api.post("/api/expense/payment-request/create", data),
|
||||
UpdatePaymentRequest: (id, data) => api.put(`/api/Expense/payment-request/edit/${id}`, data),
|
||||
GetPaymentRequest: (id) => api.get(`/api/Expense/get/payment-request/details/${id}`),
|
||||
GetPaymentRequestFilter: () => api.get('/api/Expense/payment-request/filter'),
|
||||
ActionOnPaymentRequest: (data) => api.post('/api/Expense/payment-request/action', data),
|
||||
CreatePaymentRequest: (data) =>
|
||||
api.post("/api/expense/payment-request/create", data),
|
||||
UpdatePaymentRequest: (id, data) =>
|
||||
api.put(`/api/Expense/payment-request/edit/${id}`, data),
|
||||
GetPaymentRequest: (id) =>
|
||||
api.get(`/api/Expense/get/payment-request/details/${id}`),
|
||||
GetPaymentRequestFilter: () => api.get("/api/Expense/payment-request/filter"),
|
||||
ActionOnPaymentRequest: (data) =>
|
||||
api.post("/api/Expense/payment-request/action", data),
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Advance Payment
|
||||
GetTranctionList: () => api.get(`/get/transactions/${employeeId}`)
|
||||
GetTranctionList: () => api.get(`/get/transactions/${employeeId}`),
|
||||
//#endregion
|
||||
}
|
||||
};
|
||||
|
||||
export default ExpenseRepository;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user