From b5fb48104c584be2b17c67ac8239ad7e3324666f Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Thu, 27 Nov 2025 17:23:13 +0530 Subject: [PATCH] Adding Export functionality in Recurring Expense. --- .../RecurringExpense/RecurringExpenseList.jsx | 227 +++++++++--------- .../handleRecurringExpenseExport.jsx | 64 +++++ .../RecurringExpense/RecurringExpensePage.jsx | 67 +++++- 3 files changed, 241 insertions(+), 117 deletions(-) create mode 100644 src/components/RecurringExpense/handleRecurringExpenseExport.jsx diff --git a/src/components/RecurringExpense/RecurringExpenseList.jsx b/src/components/RecurringExpense/RecurringExpenseList.jsx index a4c45fba..a48361fd 100644 --- a/src/components/RecurringExpense/RecurringExpenseList.jsx +++ b/src/components/RecurringExpense/RecurringExpenseList.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { EXPENSE_DRAFT, EXPENSE_REJECTEDBY, @@ -18,7 +18,7 @@ import { useRecurringExpenseList } from "../../hooks/useExpense"; import Pagination from "../common/Pagination"; import { SpinnerLoader } from "../common/Loader"; -const RecurringExpenseList = ({ search, filterStatuses }) => { +const RecurringExpenseList = ({ search, filterStatuses, tableRef, onDataFiltered }) => { const { setManageRequest, setVieRequest, setViewRecurring } = useRecurringExpenseContext(); const navigate = useNavigate(); @@ -70,9 +70,8 @@ const RecurringExpenseList = ({ search, filterStatuses }) => { align: "text-end", getValue: (e) => e?.amount - ? `${ - e?.currency?.symbol ? e.currency.symbol + " " : "" - }${e.amount.toLocaleString()}` + ? `${e?.currency?.symbol ? e.currency.symbol + " " : "" + }${e.amount.toLocaleString()}` : "N/A", }, { @@ -112,6 +111,17 @@ const RecurringExpenseList = ({ search, filterStatuses }) => { debouncedSearch ); + const filteredData = useMemo( + () => + data?.data?.filter((item) => filterStatuses.includes(item?.status?.id)) || + [], + [data?.data, filterStatuses] + ); + + useEffect(() => { + onDataFiltered(filteredData); + }, [filteredData, onDataFiltered]); + const paginate = (page) => { if (page >= 1 && page <= (data?.totalPages ?? 1)) { setCurrentPage(page); @@ -150,10 +160,6 @@ const RecurringExpenseList = ({ search, filterStatuses }) => { ); }; - const filteredData = data?.data?.filter((item) => - filterStatuses.includes(item?.status?.id) - ); - const handleDelete = (id) => { setDeletingId(id); DeleteExpense( @@ -166,7 +172,6 @@ const RecurringExpenseList = ({ search, filterStatuses }) => { } ); }; -console.log("Tanish",filteredData) return ( <> {IsDeleteModalOpen && ( @@ -181,111 +186,111 @@ console.log("Tanish",filteredData) /> )} -
+
-
- {Array.isArray(filteredData) && filteredData.length > 0 && ( - - - - {recurringExpenseColumns.map((col) => ( - - ))} - - - - - - {filteredData?.length > 0 ? ( - filteredData?.map((recurringExpense) => ( - - {recurringExpenseColumns.map((col) => ( - - ))} - - - )) - ) : ( +
+ {Array.isArray(filteredData) && filteredData.length > 0 && ( +
- {col.label} - Action
- {col?.customRender - ? col?.customRender(recurringExpense) - : col?.getValue(recurringExpense)} - -
- - setViewRecurring({ - recurringId: recurringExpense?.id, - view: true, - }) - } - > - -
- -
    -
  • - setManageRequest({ - IsOpen: true, - RecurringId: recurringExpense?.id, - }) - } - > - - - Modify - -
  • - -
  • { - setIsDeleteModalOpen(true); - setDeletingId(recurringExpense.id); - }} - > - - - Delete - -
  • -
-
-
-
+ - + {recurringExpenseColumns.map((col) => ( + + ))} + - )} - -
+ {col.label} + Action
- )} - {!filteredData || - filteredData.length === 0 - && ( -
- {isError ? (

{error.message}

):(

No Recurring Expense Found

)} -
+ + + + {filteredData?.length > 0 ? ( + filteredData?.map((recurringExpense) => ( + + {recurringExpenseColumns.map((col) => ( + + {col?.customRender + ? col?.customRender(recurringExpense) + : col?.getValue(recurringExpense)} + + ))} + +
+ + setViewRecurring({ + recurringId: recurringExpense?.id, + view: true, + }) + } + > + +
+ +
    +
  • + setManageRequest({ + IsOpen: true, + RecurringId: recurringExpense?.id, + }) + } + > + + + Modify + +
  • + +
  • { + setIsDeleteModalOpen(true); + setDeletingId(recurringExpense.id); + }} + > + + + Delete + +
  • +
+
+
+ + + )) + ) : ( + + + + )} + + )} -
+ {!filteredData || + filteredData.length === 0 + && ( +
+ {isError ? (

{error.message}

) : (

No Recurring Expense Found

)} +
+ )} +
{/* Pagination */} diff --git a/src/components/RecurringExpense/handleRecurringExpenseExport.jsx b/src/components/RecurringExpense/handleRecurringExpenseExport.jsx new file mode 100644 index 00000000..af9537c7 --- /dev/null +++ b/src/components/RecurringExpense/handleRecurringExpenseExport.jsx @@ -0,0 +1,64 @@ +import moment from "moment"; +import { exportToCSV,exportToExcel,exportToPDF,printTable } from "../../utils/tableExportUtils"; +import { FREQUENCY_FOR_RECURRING } from "../../utils/constants"; + +const handleRecurringExpenseExport = (type, expenses, tableRef) => { + if (!expenses || expenses.length === 0) return; + + // Mapped Export Data + const exportData = expenses.map((item) => ({ + Category: item?.expenseCategory?.name ?? "-", + Title: item?.title ?? "-", + Payee: item?.payee ?? "-", + Frequency: + item?.frequency !== undefined && item?.frequency !== null + ? FREQUENCY_FOR_RECURRING[item?.frequency] ?? "-" + : "-", + Amount: item?.amount ? item.amount.toLocaleString() : "-", + Currency: item?.currency?.symbol ?? "-", + "Next Generation Date": item?.nextGenerationDate + ? moment(item.nextGenerationDate).format("DD-MMM-YYYY") + : "-", + Status: item?.status?.name ?? "-", + "Created At": item?.createdAt + ? moment(item.createdAt).format("DD-MMM-YYYY") + : "-", + })); + + // COLUMN ORDER + const columns = [ + "Category", + "Title", + "Payee", + "Frequency", + "Amount", + "Currency", + "Next Generation Date", + "Status", + "Created At", + ]; + + switch (type) { + case "csv": + exportToCSV(exportData, "recurring-expense", columns); + break; + + case "excel": + exportToExcel(exportData, "recurring-expense", columns); + break; + + case "pdf": + exportToPDF(exportData, "recurring-expense", columns); + break; + + case "print": + if (tableRef?.current) printTable(tableRef.current); + break; + + default: + console.warn("Unhandled export type:", type); + break; + } +}; + +export default handleRecurringExpenseExport; diff --git a/src/pages/RecurringExpense/RecurringExpensePage.jsx b/src/pages/RecurringExpense/RecurringExpensePage.jsx index 12c07e10..db923657 100644 --- a/src/pages/RecurringExpense/RecurringExpensePage.jsx +++ b/src/pages/RecurringExpense/RecurringExpensePage.jsx @@ -1,4 +1,4 @@ -import React, { createContext, useState, useEffect, useContext } from "react"; +import React, { createContext, useState, useEffect, useContext, useRef } from "react"; import Breadcrumb from "../../components/common/Breadcrumb"; import GlobalModel from "../../components/common/GlobalModel"; import { useFab } from "../../Context/FabContext"; @@ -7,6 +7,7 @@ import RecurringExpenseList from "../../components/RecurringExpense/RecurringExp import { PAYEE_RECURRING_EXPENSE } from "../../utils/constants"; import { SearchRecurringExpenseSchema } from "../../components/RecurringExpense/RecurringExpenseSchema"; import ViewRecurringExpense from "../../components/RecurringExpense/ViewRecurringExpense"; +import handleRecurringExpenseExport from "../../components/RecurringExpense/handleRecurringExpenseExport"; export const RecurringExpenseContext = createContext(); export const useRecurringExpenseContext = () => { @@ -23,6 +24,10 @@ const RecurringExpensePage = () => { IsOpen: null, RecurringId: null, }); + const tableRef = useRef(null); + + const [filteredData, setFilteredData] = useState([]); + const [viewRecurring, setViewRecurring] = useState({ view: false, recurringId: null, @@ -44,6 +49,12 @@ const RecurringExpensePage = () => { prev.includes(id) ? prev.filter((s) => s !== id) : [...prev, id] ); }; + + + const handleExport = (type) => { + handleRecurringExpenseExport(type, filteredData, tableRef); + }; + return (
@@ -56,8 +67,8 @@ const RecurringExpensePage = () => { /> {/* Top Bar */} -
-
+
+
{/* Left Column: Search + Filter */}
@@ -67,7 +78,7 @@ const RecurringExpensePage = () => { className="form-control form-control-sm w-auto" placeholder="Search Recurring Expense" value={search} - style={{minWidth:"200px"}} + style={{ minWidth: "200px" }} onChange={(e) => setSearch(e.target.value)} /> @@ -99,8 +110,8 @@ const RecurringExpensePage = () => {
- {/* Right Column: Add Button */} -
+ {/* Right Column: Add Button + 3-Dots Menu */} +
+ + {/* 3-Dots Dropdown */} +
+ + +
    +
  • + +
  • + +

  • + +
  • + +
  • + +
  • + +
  • + +
  • + +
  • +
+
+
+
@@ -124,6 +177,8 @@ const RecurringExpensePage = () => { {ManageRequest.IsOpen && (