We are creating filter chips in the Payment Request filter panel.
This commit is contained in:
parent
297e0712bc
commit
598601c515
60
src/components/PaymentRequest/PaymentRequestFilterChips.jsx
Normal file
60
src/components/PaymentRequest/PaymentRequestFilterChips.jsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import React, { useMemo } from "react";
|
||||||
|
|
||||||
|
const PaymentRequestFilterChips = ({ filters, filterData, removeFilterChip, clearFilter }) => {
|
||||||
|
const data = filterData?.data || filterData || {};
|
||||||
|
|
||||||
|
const filterChips = useMemo(() => {
|
||||||
|
const chips = [];
|
||||||
|
|
||||||
|
const addGroup = (ids, list, label, key) => {
|
||||||
|
if (!ids?.length) return;
|
||||||
|
const items = ids.map((id) => ({
|
||||||
|
id,
|
||||||
|
name: list?.find((i) => i.id === id)?.name || id,
|
||||||
|
}));
|
||||||
|
chips.push({ key, label, items });
|
||||||
|
};
|
||||||
|
|
||||||
|
addGroup(filters.createdByIds, data.createdBy, "Created By", "createdByIds");
|
||||||
|
addGroup(filters.currencyIds, data.currency, "Currency", "currencyIds");
|
||||||
|
addGroup(filters.expenseCategoryIds, data.expenseCategory, "Expense Category", "expenseCategoryIds");
|
||||||
|
addGroup(filters.payees, data.payees, "Payees", "payees");
|
||||||
|
addGroup(filters.projectIds, data.projects, "Projects", "projectIds");
|
||||||
|
addGroup(filters.statusIds, data.status, "Status", "statusIds");
|
||||||
|
|
||||||
|
return chips;
|
||||||
|
}, [filters, filterData]);
|
||||||
|
|
||||||
|
if (!filterChips.length) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="d-flex flex-wrap align-items-center gap-2">
|
||||||
|
{filterChips.map((chipGroup) => (
|
||||||
|
<div key={chipGroup.key} className="d-flex align-items-center flex-wrap">
|
||||||
|
<span className="fw-semibold me-2">{chipGroup.label}:</span>
|
||||||
|
{chipGroup.items.map((item) => (
|
||||||
|
<span
|
||||||
|
key={item.id}
|
||||||
|
className="d-flex align-items-center bg-light rounded px-2 py-1 me-1"
|
||||||
|
>
|
||||||
|
<span>{item.name}</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn-close btn-close-white btn-sm ms-2"
|
||||||
|
style={{
|
||||||
|
filter: "invert(1) grayscale(1)",
|
||||||
|
opacity: 0.7,
|
||||||
|
fontSize: "0.6rem",
|
||||||
|
}}
|
||||||
|
onClick={() => removeFilterChip(chipGroup.key, item.id)}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PaymentRequestFilterChips;
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useState, useMemo } from "react";
|
import React, { useEffect, useState, useMemo, forwardRef, useImperativeHandle } from "react";
|
||||||
import { FormProvider, useForm, Controller } from "react-hook-form";
|
import { FormProvider, useForm, Controller } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { defaultPaymentRequestFilter,SearchPaymentRequestSchema } from "./PaymentRequestSchema";
|
import { defaultPaymentRequestFilter, SearchPaymentRequestSchema } from "./PaymentRequestSchema";
|
||||||
|
|
||||||
import DateRangePicker, { DateRangePicker1 } from "../common/DateRangePicker";
|
import DateRangePicker, { DateRangePicker1 } from "../common/DateRangePicker";
|
||||||
import SelectMultiple from "../common/SelectMultiple";
|
import SelectMultiple from "../common/SelectMultiple";
|
||||||
@ -13,7 +13,7 @@ import moment from "moment";
|
|||||||
import { usePaymentRequestFilter } from "../../hooks/useExpense";
|
import { usePaymentRequestFilter } from "../../hooks/useExpense";
|
||||||
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
||||||
|
|
||||||
const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => {
|
const PaymentRequestFilterPanel = forwardRef(({ onApply, handleGroupBy, setFilterdata, clearFilter }, ref) => {
|
||||||
const { status } = useParams();
|
const { status } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const selectedProjectId = useSelector(
|
const selectedProjectId = useSelector(
|
||||||
@ -38,10 +38,23 @@ const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => {
|
|||||||
const [selectedGroup, setSelectedGroup] = useState(groupByList[6]);
|
const [selectedGroup, setSelectedGroup] = useState(groupByList[6]);
|
||||||
const [resetKey, setResetKey] = useState(0);
|
const [resetKey, setResetKey] = useState(0);
|
||||||
|
|
||||||
|
const dynamicDefaultFilter = useMemo(() => {
|
||||||
|
return {
|
||||||
|
...defaultPaymentRequestFilter,
|
||||||
|
projectIds: defaultPaymentRequestFilter.projectIds || [],
|
||||||
|
statusIds: status ? [status] : defaultPaymentRequestFilter.statusIds || [],
|
||||||
|
createdByIds: defaultPaymentRequestFilter.createdByIds || [],
|
||||||
|
currencyIds: defaultPaymentRequestFilter.currencyIds || [],
|
||||||
|
expenseCategoryIds: defaultPaymentRequestFilter.expenseCategoryIds || [],
|
||||||
|
payees: defaultPaymentRequestFilter.payees || [],
|
||||||
|
startDate: defaultPaymentRequestFilter.startDate,
|
||||||
|
endDate: defaultPaymentRequestFilter.endDate,
|
||||||
|
};
|
||||||
|
}, [status, selectedProjectId]);
|
||||||
|
|
||||||
const methods = useForm({
|
const methods = useForm({
|
||||||
resolver: zodResolver(SearchPaymentRequestSchema),
|
resolver: zodResolver(SearchPaymentRequestSchema),
|
||||||
defaultValues: defaultPaymentRequestFilter,
|
defaultValues: dynamicDefaultFilter,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { control, handleSubmit, reset, setValue, watch } = methods;
|
const { control, handleSubmit, reset, setValue, watch } = methods;
|
||||||
@ -52,12 +65,28 @@ const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleGroupChange = (e) => {
|
const handleGroupChange = (e) => {
|
||||||
const group = groupByList.find((g) => g.id === e.target.value);
|
const group = groupByList.find((g) => g.id === e.target.value);
|
||||||
if (group) setSelectedGroup(group);
|
if (group) setSelectedGroup(group);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
resetFieldValue: (name, value) => {
|
||||||
|
// Reset specific field
|
||||||
|
if (value !== undefined) {
|
||||||
|
setValue(name, value);
|
||||||
|
} else {
|
||||||
|
reset({ ...methods.getValues(), [name]: defaultFilter[name] });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getValues: methods.getValues, // optional, to read current filter state
|
||||||
|
}));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data && setFilterdata) {
|
||||||
|
setFilterdata(data);
|
||||||
|
}
|
||||||
|
}, [data, setFilterdata]);
|
||||||
|
|
||||||
const onSubmit = (formData) => {
|
const onSubmit = (formData) => {
|
||||||
onApply({
|
onApply({
|
||||||
@ -197,6 +226,6 @@ const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => {
|
|||||||
</FormProvider>
|
</FormProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default PaymentRequestFilterPanel;
|
export default PaymentRequestFilterPanel;
|
||||||
@ -20,8 +20,9 @@ import { useNavigate } from "react-router-dom";
|
|||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import Error from "../common/Error";
|
import Error from "../common/Error";
|
||||||
import Pagination from "../common/Pagination";
|
import Pagination from "../common/Pagination";
|
||||||
|
import PaymentRequestFilterChips from "./PaymentRequestFilterChips";
|
||||||
|
|
||||||
const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
const PaymentRequestList = ({ filters, filterData, removeFilterChip, clearFilter, search, groupBy = "submittedBy" }) => {
|
||||||
const { setManageRequest, setVieRequest } = usePaymentRequestContext();
|
const { setManageRequest, setVieRequest } = usePaymentRequestContext();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
@ -92,33 +93,6 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
|||||||
align: "text-start",
|
align: "text-start",
|
||||||
getValue: (e) => e.title || "N/A",
|
getValue: (e) => e.title || "N/A",
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// key: "SubmittedBy",
|
|
||||||
// label: "Submitted By",
|
|
||||||
// align: "text-start",
|
|
||||||
// getValue: (e) =>
|
|
||||||
// `${e.createdBy?.firstName ?? ""} ${
|
|
||||||
// e.createdBy?.lastName ?? ""
|
|
||||||
// }`.trim() || "N/A",
|
|
||||||
// customRender: (e) => (
|
|
||||||
// <div
|
|
||||||
// className="d-flex align-items-center cursor-pointer"
|
|
||||||
// onClick={() => navigate(`/employee/${e.createdBy?.id}`)}
|
|
||||||
// >
|
|
||||||
// <Avatar
|
|
||||||
// size="xs"
|
|
||||||
// classAvatar="m-0"
|
|
||||||
// firstName={e.createdBy?.firstName}
|
|
||||||
// lastName={e.createdBy?.lastName}
|
|
||||||
// />
|
|
||||||
// <span className="text-truncate">
|
|
||||||
// {`${e.createdBy?.firstName ?? ""} ${
|
|
||||||
// e.createdBy?.lastName ?? ""
|
|
||||||
// }`.trim() || "N/A"}
|
|
||||||
// </span>
|
|
||||||
// </div>
|
|
||||||
// ),
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
key: "createdAt",
|
key: "createdAt",
|
||||||
label: "Created At",
|
label: "Created At",
|
||||||
@ -250,6 +224,14 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
|||||||
)}
|
)}
|
||||||
<div className="card page-min-h table-responsive px-sm-4">
|
<div className="card page-min-h table-responsive px-sm-4">
|
||||||
<div className="card-datatable mx-2" id="payment-request-table ">
|
<div className="card-datatable mx-2" id="payment-request-table ">
|
||||||
|
<div className="col-12 mb-2 mt-2">
|
||||||
|
<PaymentRequestFilterChips
|
||||||
|
filters={filters}
|
||||||
|
filterData={filterData}
|
||||||
|
removeFilterChip={removeFilterChip}
|
||||||
|
clearFilter={clearFilter}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<table className="table border-top dataTable text-nowrap align-middle">
|
<table className="table border-top dataTable text-nowrap align-middle">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
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 ManagePaymentRequest from "../../components/PaymentRequest/ManagePaymentRequest";
|
import ManagePaymentRequest from "../../components/PaymentRequest/ManagePaymentRequest";
|
||||||
import ExpenseFilterPanel from "../../components/Expenses/ExpenseFilterPanel";
|
|
||||||
import { useFab } from "../../Context/FabContext";
|
import { useFab } from "../../Context/FabContext";
|
||||||
import PaymentRequestList from "../../components/PaymentRequest/PaymentRequestList";
|
import PaymentRequestList from "../../components/PaymentRequest/PaymentRequestList";
|
||||||
import PaymentRequestFilterPanel from "../../components/PaymentRequest/PaymentRequestFilterPanel";
|
import PaymentRequestFilterPanel from "../../components/PaymentRequest/PaymentRequestFilterPanel";
|
||||||
import { defaultPaymentRequestFilter,SearchPaymentRequestSchema } from "../../components/PaymentRequest/PaymentRequestSchema";
|
import { defaultPaymentRequestFilter } from "../../components/PaymentRequest/PaymentRequestSchema";
|
||||||
import ViewPaymentRequest from "../../components/PaymentRequest/ViewPaymentRequest";
|
import ViewPaymentRequest from "../../components/PaymentRequest/ViewPaymentRequest";
|
||||||
import PreviewDocument from "../../components/Expenses/PreviewDocument";
|
import PreviewDocument from "../../components/Expenses/PreviewDocument";
|
||||||
import MakeExpense from "../../components/PaymentRequest/MakeExpense";
|
import MakeExpense from "../../components/PaymentRequest/MakeExpense";
|
||||||
@ -15,26 +14,22 @@ export const PaymentRequestContext = createContext();
|
|||||||
export const usePaymentRequestContext = () => {
|
export const usePaymentRequestContext = () => {
|
||||||
const context = useContext(PaymentRequestContext);
|
const context = useContext(PaymentRequestContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error("usePaymentRequestContext must be used within an ExpenseProvider");
|
throw new Error("usePaymentRequestContext must be used within PaymentRequestContext.Provider");
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PaymentRequestPage = () => {
|
const PaymentRequestPage = () => {
|
||||||
const [ManageRequest, setManageRequest] = useState({
|
const [ManageRequest, setManageRequest] = useState({ IsOpen: null, RequestId: null });
|
||||||
IsOpen: null,
|
const [ViewRequest, setVieRequest] = useState({ view: false, requestId: null });
|
||||||
RequestId: null,
|
|
||||||
});
|
|
||||||
const [ViewRequest,setVieRequest] = useState({view:false,requestId:null})
|
|
||||||
const { setOffcanvasContent, setShowTrigger } = useFab();
|
|
||||||
const [filters, setFilters] = useState(defaultPaymentRequestFilter);
|
const [filters, setFilters] = useState(defaultPaymentRequestFilter);
|
||||||
const [ViewDocument, setDocumentView] = useState({
|
const [filterData, setFilterdata] = useState(null);
|
||||||
IsOpen: false,
|
const [ViewDocument, setDocumentView] = useState({ IsOpen: false, Image: null });
|
||||||
Image: null,
|
const [isExpenseGenerate, setIsExpenseGenerate] = useState({ IsOpen: null, RequestId: null });
|
||||||
});
|
const [modalSize, setModalSize] = useState("md");
|
||||||
const [isExpenseGenerate,setIsExpenseGenerate] = useState({IsOpen: null,
|
|
||||||
RequestId: null,})
|
|
||||||
const [modalSize,setModalSize] = useState("md")
|
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
const updatedRef = useRef();
|
||||||
|
const { setOffcanvasContent, setShowTrigger } = useFab();
|
||||||
|
|
||||||
const contextValue = {
|
const contextValue = {
|
||||||
setManageRequest,
|
setManageRequest,
|
||||||
@ -42,15 +37,21 @@ const PaymentRequestPage = () => {
|
|||||||
setDocumentView,
|
setDocumentView,
|
||||||
setModalSize,
|
setModalSize,
|
||||||
setIsExpenseGenerate,
|
setIsExpenseGenerate,
|
||||||
isExpenseGenerate
|
isExpenseGenerate,
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const clearFilter = () => setFilters(defaultPaymentRequestFilter);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
setShowTrigger(true);
|
setShowTrigger(true);
|
||||||
setOffcanvasContent(
|
setOffcanvasContent(
|
||||||
"Payment Request Filters",
|
"Payment Request Filters",
|
||||||
<PaymentRequestFilterPanel onApply={setFilters} />
|
<PaymentRequestFilterPanel
|
||||||
|
onApply={setFilters}
|
||||||
|
ref={updatedRef}
|
||||||
|
clearFilter={clearFilter}
|
||||||
|
setFilterdata={setFilterdata}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -59,6 +60,22 @@ const PaymentRequestPage = () => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleRemoveChip = (key, id) => {
|
||||||
|
setFilters((prev) => {
|
||||||
|
const updated = { ...prev };
|
||||||
|
|
||||||
|
if (Array.isArray(updated[key])) {
|
||||||
|
updated[key] = updated[key].filter((v) => v !== id);
|
||||||
|
setTimeout(() => updatedRef.current?.resetFieldValue(key, updated[key]), 0);
|
||||||
|
} else {
|
||||||
|
updated[key] = null;
|
||||||
|
setTimeout(() => updatedRef.current?.resetFieldValue(key, null), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PaymentRequestContext.Provider value={contextValue}>
|
<PaymentRequestContext.Provider value={contextValue}>
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
@ -109,6 +126,9 @@ const PaymentRequestPage = () => {
|
|||||||
<PaymentRequestList
|
<PaymentRequestList
|
||||||
search={search}
|
search={search}
|
||||||
filters={filters}
|
filters={filters}
|
||||||
|
filterData={filterData}
|
||||||
|
removeFilterChip={handleRemoveChip}
|
||||||
|
clearFilter={clearFilter}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Add/Edit Modal */}
|
{/* Add/Edit Modal */}
|
||||||
@ -116,16 +136,12 @@ const PaymentRequestPage = () => {
|
|||||||
<GlobalModel
|
<GlobalModel
|
||||||
isOpen
|
isOpen
|
||||||
size="lg"
|
size="lg"
|
||||||
closeModal={() =>
|
closeModal={() => setManageRequest({ IsOpen: null, expenseId: null })}
|
||||||
setManageRequest({ IsOpen: null, expenseId: null })
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<ManagePaymentRequest
|
<ManagePaymentRequest
|
||||||
key={ManageRequest.RequestId ?? "new"}
|
key={ManageRequest.RequestId ?? "new"}
|
||||||
requestToEdit={ManageRequest.RequestId}
|
requestToEdit={ManageRequest.RequestId}
|
||||||
closeModal={() =>
|
closeModal={() => setManageRequest({ IsOpen: null, RequestId: null })}
|
||||||
setManageRequest({ IsOpen: null, RequestId: null })
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</GlobalModel>
|
</GlobalModel>
|
||||||
)}
|
)}
|
||||||
@ -140,14 +156,16 @@ const PaymentRequestPage = () => {
|
|||||||
<ViewPaymentRequest requestId={ViewRequest?.requestId} />
|
<ViewPaymentRequest requestId={ViewRequest?.requestId} />
|
||||||
</GlobalModel>
|
</GlobalModel>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isExpenseGenerate.IsOpen && (
|
{isExpenseGenerate.IsOpen && (
|
||||||
<GlobalModel
|
<GlobalModel
|
||||||
isOpen
|
isOpen
|
||||||
size="md"
|
size="md"
|
||||||
closeModal={() => setIsExpenseGenerate({IsOpen:false, requestId: null})}
|
closeModal={() => setIsExpenseGenerate({ IsOpen: false, requestId: null })}
|
||||||
>
|
>
|
||||||
<MakeExpe
|
<MakeExpense
|
||||||
nse onClose={() => setIsExpenseGenerate({IsOpen:false, requestId: null})} />
|
onClose={() => setIsExpenseGenerate({ IsOpen: false, requestId: null })}
|
||||||
|
/>
|
||||||
</GlobalModel>
|
</GlobalModel>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -161,7 +179,6 @@ const PaymentRequestPage = () => {
|
|||||||
<PreviewDocument imageUrl={ViewDocument.Image} />
|
<PreviewDocument imageUrl={ViewDocument.Image} />
|
||||||
</GlobalModel>
|
</GlobalModel>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</PaymentRequestContext.Provider>
|
</PaymentRequestContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user