@@ -395,7 +397,7 @@ function ManagePaymentRequest({ closeModal, expenseToEdit = null }) {
{files
.filter((file) => {
- if (expenseToEdit) {
+ if (requestToEdit) {
return file.isActive;
}
return true;
@@ -420,7 +422,7 @@ function ManagePaymentRequest({ closeModal, expenseToEdit = null }) {
className="bx bx-trash bx-sm cursor-pointer text-danger"
onClick={(e) => {
e.preventDefault();
- removeFile(expenseToEdit ? file.documentId : idx);
+ removeFile(requestToEdit ? file.documentId : idx);
}}
>
@@ -458,7 +460,7 @@ function ManagePaymentRequest({ closeModal, expenseToEdit = null }) {
>
{createPending
? "Please Wait..."
- : expenseToEdit
+ : requestToEdit
? "Update"
: "Submit"}
diff --git a/src/components/PaymentRequest/PaymentRequestList.jsx b/src/components/PaymentRequest/PaymentRequestList.jsx
index 62e7b132..6356736d 100644
--- a/src/components/PaymentRequest/PaymentRequestList.jsx
+++ b/src/components/PaymentRequest/PaymentRequestList.jsx
@@ -1,20 +1,139 @@
import React, { useState } from "react";
-import { ITEMS_PER_PAGE } from "../../utils/constants";
-import { useDebounce } from "../../utils/appUtils";
-import { formatDate } from "../../utils/dateUtils";
+import { EXPENSE_DRAFT, ITEMS_PER_PAGE } from "../../utils/constants";
+import {
+ formatCurrency,
+ getColorNameFromHex,
+ useDebounce,
+} from "../../utils/appUtils";
import { usePaymentRequestList } from "../../hooks/useExpense";
-import ExpenseSkeleton, {
- ExpenseTableSkeleton,
-} from "../Expenses/ExpenseSkeleton";
+import { formatDate, formatUTCToLocalTime } from "../../utils/dateUtils";
+import Avatar from "../../components/common/Avatar";
+import { usePaymentRequestContext } from "../../pages/PaymentRequest/PaymentRequestPage";
+import { ExpenseTableSkeleton } from "../Expenses/ExpenseSkeleton";
+
+const PaymentRequestList = ({ groupBy = "submittedBy", search }) => {
+ const { setManageRequest } = usePaymentRequestContext();
+ const groupByField = (items, field) => {
+ return items.reduce((acc, item) => {
+ let key;
+ let displayField;
+
+ switch (field) {
+ case "transactionDate":
+ key = item?.transactionDate?.split("T")[0];
+ displayField = "Transaction Date";
+ break;
+ case "status":
+ key = item?.status?.displayName || "Unknown";
+ displayField = "Status";
+ break;
+ case "submittedBy":
+ key = `${item?.createdBy?.firstName ?? ""} ${
+ item.createdBy?.lastName ?? ""
+ }`.trim();
+ displayField = "Submitted By";
+ break;
+ case "project":
+ key = item?.project?.name || "Unknown Project";
+ displayField = "Project";
+ break;
+ case "paymentMode":
+ key = item?.paymentMode?.name || "Unknown Mode";
+ displayField = "Payment Mode";
+ break;
+ case "expensesType":
+ key = item?.expensesType?.name || "Unknown Type";
+ displayField = "Expense Category";
+ break;
+ case "createdAt":
+ key = item?.createdAt?.split("T")[0] || "Unknown Date";
+ displayField = "Created Date";
+ break;
+ default:
+ key = "Others";
+ displayField = "Others";
+ }
+
+ const groupKey = `${field}_${key}`; // unique key for object property
+ if (!acc[groupKey]) {
+ acc[groupKey] = { key, displayField, items: [] };
+ }
+
+ acc[groupKey].items.push(item);
+ return acc;
+ }, {});
+ };
-const PaymentRequestList = ({ setManagePaymentRequestModal, search }) => {
const paymentRequestColumns = [
- { key: "paymentRequestUID", label: "Request ID", align: "text-start mx-2" },
- { key: "title", label: "Request Title", align: "text-start" },
- { key: "payee", label: "Payee", align: "text-start" },
- { key: "createdAt", label: "Submitted On", align: "text-start" },
- { key: "amount", label: "Amount", align: "text-start" },
- { key: "expenseStatus", label: "Status", align: "text-start" },
+ {
+ key: "paymentRequestUID",
+ label: "Request ID",
+ align: "text-start mx-2",
+ getValue: (e) => e.paymentRequestUID || "N/A",
+ },
+ {
+ key: "title",
+ label: "Request Title",
+ align: "text-start",
+ getValue: (e) => e.title || "N/A",
+ },
+ // { key: "payee", label: "Payee", align: "text-start" },
+ {
+ key: "SubmittedBy",
+ label: "Submitted By",
+ align: "text-start",
+ getValue: (e) =>
+ `${e.createdBy?.firstName ?? ""} ${
+ e.createdBy?.lastName ?? ""
+ }`.trim() || "N/A",
+ customRender: (e) => (
+
navigate(`/employee/${e.createdBy?.id}`)}
+ >
+
+
+ {`${e.createdBy?.firstName ?? ""} ${
+ e.createdBy?.lastName ?? ""
+ }`.trim() || "N/A"}
+
+
+ ),
+ },
+ {
+ key: "createdAt",
+ label: "Submitted On",
+ align: "text-start",
+ getValue: (e) => formatUTCToLocalTime(e?.createdAt),
+ },
+ {
+ key: "amount",
+ label: " Amount",
+ align: "text-start",
+ getValue: (e) => (
+ <>{formatCurrency(e?.amount, e.currency.currencyCode)}>
+ ),
+ align: "text-end px-3",
+ },
+ {
+ key: "expenseStatus",
+ label: "Status",
+ align: "text-center",
+ getValue: (e) => (
+
+ {e?.expenseStatus?.name || "Unknown"}
+
+ ),
+ },
];
const [currentPage, setCurrentPage] = useState(1);
@@ -40,16 +159,37 @@ const PaymentRequestList = ({ setManagePaymentRequestModal, search }) => {
);
}
const header = [
- "Expense ID",
- "Expense Category",
- "Payment Mode",
- "Sumitted By",
- "Submitted",
+ "Request ID",
+ "Request Title",
+ "Submitted By",
+ "Submitted On",
"Amount",
"Status",
"Action",
];
if (isLoading) return
;
+
+ const grouped = groupBy
+ ? groupByField(data?.data ?? [], groupBy)
+ : { All: data?.data ?? [] };
+ const IsGroupedByDate = [
+ { key: "transactionDate", displayField: "Transaction Date" },
+ { key: "createdAt", displayField: "created Date" },
+ ]?.includes(groupBy);
+
+ const canEditExpense = (paymentRequest) => {
+ return (
+ (paymentRequest?.status?.id === EXPENSE_DRAFT ||
+ EXPENSE_REJECTEDBY.includes(paymentRequest?.status?.id)) &&
+ paymentRequest?.createdBy?.id === SelfId
+ );
+ };
+ const canDetetExpense = (expense) => {
+ return (
+ expense?.status?.id === EXPENSE_DRAFT && expense?.createdBy?.id === SelfId
+ );
+ };
+
return (
@@ -66,70 +206,105 @@ const PaymentRequestList = ({ setManagePaymentRequestModal, search }) => {
- {isLoading || isFetching ? (
-
- |
- Loading Payment Requests...
- |
-
- ) : paymentRequestData.length > 0 ? (
- paymentRequestData.map((row) => (
-
- | {row.paymentRequestUID} |
- {row.title} |
- {row.payee} |
- {formatDate(row.createdAt)} |
-
- {row.currency?.symbol}
- {row.amount}
- |
-
-
- {row.expenseStatus?.displayName || "Unknown"}
-
- |
-
-
-
- console.log(
- "View clicked for:",
- row.paymentRequestUID
+ {Object.keys(grouped).length > 0 ? (
+ Object.values(grouped).map(({ key, displayField, items }) => (
+
+
+ |
+
+ {" "}
+
+ {displayField} :{" "}
+ {" "}
+
+ {IsGroupedByDate ? formatUTCToLocalTime(key) : key}
+
+
+ |
+
+ {items?.map((paymentRequest) => (
+
+ {paymentRequestColumns.map(
+ (col) =>
+ (col.isAlwaysVisible || groupBy !== col.key) && (
+ |
+ {col?.customRender
+ ? col?.customRender(paymentRequest)
+ : col?.getValue(paymentRequest)}
+ |
)
- }
- >
+ )}
+
+
+
+ setViewExpense({
+ expenseId: paymentRequest.id,
+ view: true,
+ })
+ }
+ >
-
- setManagePaymentRequestModal({
- IsOpen: true,
- expenseId: row.id, // Pass ID for editing
- })
- }
- >
-
- |
-
+
+
+
+ -
+ setManageRequest({
+ IsOpen: true,
+ RequestId: paymentRequest.id,
+ })
+ }
+ >
+
+
+ Modify
+
+
+
+ - {
+ setIsDeleteModalOpen(true);
+ setDeletingId(paymentRequest.id);
+ }}
+ >
+
+
+ Delete
+
+
+
+
+
+ |
+
+ ))}
+
))
) : (
- |
- No Payment Requests Found
+ |
+
|
)}
diff --git a/src/components/common/Tooltip.jsx b/src/components/common/Tooltip.jsx
new file mode 100644
index 00000000..527e839d
--- /dev/null
+++ b/src/components/common/Tooltip.jsx
@@ -0,0 +1,24 @@
+import { useEffect } from "react";
+
+const Tooltip = ({ text, placement = "top", children }) => {
+ useEffect(() => {
+
+ const el = document.querySelector(`[data-tooltip-id="${text}"]`);
+ if (el) {
+ new window.bootstrap.Tooltip(el);
+ }
+ }, [text]);
+
+ return (
+
+ {children}
+
+ );
+};
+export default Tooltip;
\ No newline at end of file
diff --git a/src/components/master/ManageExpenseType.jsx b/src/components/master/ManageExpenseCategory.jsx
similarity index 68%
rename from src/components/master/ManageExpenseType.jsx
rename to src/components/master/ManageExpenseCategory.jsx
index a98cedf3..d200c0dd 100644
--- a/src/components/master/ManageExpenseType.jsx
+++ b/src/components/master/ManageExpenseCategory.jsx
@@ -3,18 +3,25 @@ import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import {
- useCreateExpenseType,
- useUpdateExpenseType,
+ useCreateExpenseCategory,
+ useUpdateExpenseCategory,
} from "../../hooks/masterHook/useMaster";
import Label from "../common/Label";
+import Tooltip from "../common/Tooltip";
+
+
+
+
+
const ExpnseSchema = z.object({
name: z.string().min(1, { message: "Name is required" }),
noOfPersonsRequired: z.boolean().default(false),
+ isAttachmentRequried: z.boolean().default(false),
description: z.string().min(1, { message: "Description is required" }),
});
-const ManageExpenseType = ({ data = null, onClose }) => {
+const ManageExpenseCategory = ({ data = null, onClose }) => {
const {
register,
handleSubmit,
@@ -22,12 +29,12 @@ const ManageExpenseType = ({ data = null, onClose }) => {
formState: { errors },
} = useForm({
resolver: zodResolver(ExpnseSchema),
- defaultValues: { name: "", noOfPersonsRequired: false, description: "" },
+ defaultValues: { name: "", noOfPersonsRequired: false,isAttachmentRequried:false, description: "" },
});
- const { mutate: UpdateExpenseType, isPending:isPendingUpdate } = useUpdateExpenseType(
+ const { mutate: UpdateExpenseType, isPending:isPendingUpdate } = useUpdateExpenseCategory(
() => onClose?.()
);
- const { mutate: CreateExpenseType, isPending } = useCreateExpenseType(() =>
+ const { mutate: CreateExpenseType, isPending } = useCreateExpenseCategory(() =>
onClose?.()
);
@@ -47,6 +54,7 @@ const ManageExpenseType = ({ data = null, onClose }) => {
reset({
name: data.name ?? "",
noOfPersonsRequired: data.noOfPersonsRequired ?? false,
+ isAttachmentRequried:data.isAttachmentRequried ?? false,
description: data.description ?? "",
});
}
@@ -76,14 +84,37 @@ const ManageExpenseType = ({ data = null, onClose }) => {
{errors.description.message}
)}
-