Adding Export functionality in Recurring Expense.
This commit is contained in:
parent
359a787bdb
commit
b5fb48104c
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import {
|
import {
|
||||||
EXPENSE_DRAFT,
|
EXPENSE_DRAFT,
|
||||||
EXPENSE_REJECTEDBY,
|
EXPENSE_REJECTEDBY,
|
||||||
@ -18,7 +18,7 @@ import { useRecurringExpenseList } from "../../hooks/useExpense";
|
|||||||
import Pagination from "../common/Pagination";
|
import Pagination from "../common/Pagination";
|
||||||
import { SpinnerLoader } from "../common/Loader";
|
import { SpinnerLoader } from "../common/Loader";
|
||||||
|
|
||||||
const RecurringExpenseList = ({ search, filterStatuses }) => {
|
const RecurringExpenseList = ({ search, filterStatuses, tableRef, onDataFiltered }) => {
|
||||||
const { setManageRequest, setVieRequest, setViewRecurring } =
|
const { setManageRequest, setVieRequest, setViewRecurring } =
|
||||||
useRecurringExpenseContext();
|
useRecurringExpenseContext();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -70,8 +70,7 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
|
|||||||
align: "text-end",
|
align: "text-end",
|
||||||
getValue: (e) =>
|
getValue: (e) =>
|
||||||
e?.amount
|
e?.amount
|
||||||
? `${
|
? `${e?.currency?.symbol ? e.currency.symbol + " " : ""
|
||||||
e?.currency?.symbol ? e.currency.symbol + " " : ""
|
|
||||||
}${e.amount.toLocaleString()}`
|
}${e.amount.toLocaleString()}`
|
||||||
: "N/A",
|
: "N/A",
|
||||||
},
|
},
|
||||||
@ -112,6 +111,17 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
|
|||||||
debouncedSearch
|
debouncedSearch
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const filteredData = useMemo(
|
||||||
|
() =>
|
||||||
|
data?.data?.filter((item) => filterStatuses.includes(item?.status?.id)) ||
|
||||||
|
[],
|
||||||
|
[data?.data, filterStatuses]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onDataFiltered(filteredData);
|
||||||
|
}, [filteredData, onDataFiltered]);
|
||||||
|
|
||||||
const paginate = (page) => {
|
const paginate = (page) => {
|
||||||
if (page >= 1 && page <= (data?.totalPages ?? 1)) {
|
if (page >= 1 && page <= (data?.totalPages ?? 1)) {
|
||||||
setCurrentPage(page);
|
setCurrentPage(page);
|
||||||
@ -150,10 +160,6 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredData = data?.data?.filter((item) =>
|
|
||||||
filterStatuses.includes(item?.status?.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleDelete = (id) => {
|
const handleDelete = (id) => {
|
||||||
setDeletingId(id);
|
setDeletingId(id);
|
||||||
DeleteExpense(
|
DeleteExpense(
|
||||||
@ -166,7 +172,6 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
console.log("Tanish",filteredData)
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{IsDeleteModalOpen && (
|
{IsDeleteModalOpen && (
|
||||||
@ -181,7 +186,7 @@ console.log("Tanish",filteredData)
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="card page-min-h table-responsive px-sm-4">
|
<div className="card page-min-h table-responsive px-sm-4" ref={tableRef}>
|
||||||
<div className="card-datatable" id="payment-request-table">
|
<div className="card-datatable" id="payment-request-table">
|
||||||
<div className="mx-2">
|
<div className="mx-2">
|
||||||
{Array.isArray(filteredData) && filteredData.length > 0 && (
|
{Array.isArray(filteredData) && filteredData.length > 0 && (
|
||||||
@ -193,7 +198,7 @@ console.log("Tanish",filteredData)
|
|||||||
{col.label}
|
{col.label}
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
<th className="text-center">Action</th>
|
<th className="text-end">Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
@ -215,8 +220,8 @@ console.log("Tanish",filteredData)
|
|||||||
: col?.getValue(recurringExpense)}
|
: col?.getValue(recurringExpense)}
|
||||||
</td>
|
</td>
|
||||||
))}
|
))}
|
||||||
<td className="sticky-action-column bg-white">
|
<td className="sticky-action-column bg-white text-end">
|
||||||
<div className="d-flex flex-row gap-2 gap-0">
|
<div className="d-flex justify-content-end flex-row gap-2 gap-0">
|
||||||
<i
|
<i
|
||||||
className="bx bx-show text-primary cursor-pointer"
|
className="bx bx-show text-primary cursor-pointer"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
|
|||||||
@ -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;
|
||||||
@ -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 Breadcrumb from "../../components/common/Breadcrumb";
|
||||||
import GlobalModel from "../../components/common/GlobalModel";
|
import GlobalModel from "../../components/common/GlobalModel";
|
||||||
import { useFab } from "../../Context/FabContext";
|
import { useFab } from "../../Context/FabContext";
|
||||||
@ -7,6 +7,7 @@ import RecurringExpenseList from "../../components/RecurringExpense/RecurringExp
|
|||||||
import { PAYEE_RECURRING_EXPENSE } from "../../utils/constants";
|
import { PAYEE_RECURRING_EXPENSE } from "../../utils/constants";
|
||||||
import { SearchRecurringExpenseSchema } from "../../components/RecurringExpense/RecurringExpenseSchema";
|
import { SearchRecurringExpenseSchema } from "../../components/RecurringExpense/RecurringExpenseSchema";
|
||||||
import ViewRecurringExpense from "../../components/RecurringExpense/ViewRecurringExpense";
|
import ViewRecurringExpense from "../../components/RecurringExpense/ViewRecurringExpense";
|
||||||
|
import handleRecurringExpenseExport from "../../components/RecurringExpense/handleRecurringExpenseExport";
|
||||||
|
|
||||||
export const RecurringExpenseContext = createContext();
|
export const RecurringExpenseContext = createContext();
|
||||||
export const useRecurringExpenseContext = () => {
|
export const useRecurringExpenseContext = () => {
|
||||||
@ -23,6 +24,10 @@ const RecurringExpensePage = () => {
|
|||||||
IsOpen: null,
|
IsOpen: null,
|
||||||
RecurringId: null,
|
RecurringId: null,
|
||||||
});
|
});
|
||||||
|
const tableRef = useRef(null);
|
||||||
|
|
||||||
|
const [filteredData, setFilteredData] = useState([]);
|
||||||
|
|
||||||
const [viewRecurring, setViewRecurring] = useState({
|
const [viewRecurring, setViewRecurring] = useState({
|
||||||
view: false,
|
view: false,
|
||||||
recurringId: null,
|
recurringId: null,
|
||||||
@ -44,6 +49,12 @@ const RecurringExpensePage = () => {
|
|||||||
prev.includes(id) ? prev.filter((s) => s !== id) : [...prev, id]
|
prev.includes(id) ? prev.filter((s) => s !== id) : [...prev, id]
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handleExport = (type) => {
|
||||||
|
handleRecurringExpenseExport(type, filteredData, tableRef);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RecurringExpenseContext.Provider value={contextValue}>
|
<RecurringExpenseContext.Provider value={contextValue}>
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
@ -56,8 +67,8 @@ const RecurringExpensePage = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Top Bar */}
|
{/* Top Bar */}
|
||||||
<div className="card my-3 px-sm-4 px-0">
|
<div className="card my-3 px-sm-0 px-0">
|
||||||
<div className="card-body py-2 px-1 mx-2">
|
<div className="card-body py-2 px-5">
|
||||||
<div className="row align-items-center mb-0">
|
<div className="row align-items-center mb-0">
|
||||||
{/* Left Column: Search + Filter */}
|
{/* Left Column: Search + Filter */}
|
||||||
<div className="col-md-8 col-sm-12 mb-2 mb-md-0">
|
<div className="col-md-8 col-sm-12 mb-2 mb-md-0">
|
||||||
@ -99,8 +110,8 @@ const RecurringExpensePage = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Column: Add Button */}
|
{/* Right Column: Add Button + 3-Dots Menu */}
|
||||||
<div className="col-md-4 col-sm-12 text-md-end text-end">
|
<div className="col-md-4 col-sm-12 text-md-end text-end d-flex justify-content-end align-items-center gap-0">
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-primary"
|
className="btn btn-sm btn-primary"
|
||||||
type="button"
|
type="button"
|
||||||
@ -116,7 +127,49 @@ const RecurringExpensePage = () => {
|
|||||||
Add Recurring Expense
|
Add Recurring Expense
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{/* 3-Dots Dropdown */}
|
||||||
|
<div className="dropdown">
|
||||||
|
<button
|
||||||
|
className="btn btn-icon"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="dropdown"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<i className="bx bx-dots-vertical-rounded bx-md"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<ul className="dropdown-menu dropdown-menu-end shadow-sm" style={{ minWidth: "220px" }}>
|
||||||
|
<li>
|
||||||
|
<button className="dropdown-item" onClick={() => handleExport("print")}>
|
||||||
|
<i className="bx bx-printer me-2"></i> Print
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li><hr className="dropdown-divider" /></li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<button className="dropdown-item" onClick={() => handleExport("csv")}>
|
||||||
|
<i className="bx bx-file me-2"></i> CSV
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<button className="dropdown-item" onClick={() => handleExport("excel")}>
|
||||||
|
<i className="bx bxs-file-export me-2"></i> Excel
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<button className="dropdown-item" onClick={() => handleExport("pdf")}>
|
||||||
|
<i className="bx bxs-file-pdf me-2"></i> PDF
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -124,6 +177,8 @@ const RecurringExpensePage = () => {
|
|||||||
<RecurringExpenseList
|
<RecurringExpenseList
|
||||||
filterStatuses={selectedStatuses}
|
filterStatuses={selectedStatuses}
|
||||||
search={search}
|
search={search}
|
||||||
|
tableRef={tableRef}
|
||||||
|
onDataFiltered={setFilteredData}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{ManageRequest.IsOpen && (
|
{ManageRequest.IsOpen && (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user