From ee1cf01743bcb50085db96e6986e8e0931e1cbd4 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Sat, 22 Nov 2025 19:17:47 +0530 Subject: [PATCH] added filter - sorting, and groupby --- .../collections/PmGridCollection.jsx | 5 +- src/components/common/Chips.jsx | 105 +++++++++------ src/repositories/ColllectionRepository.jsx | 24 +++- src/services/pmsGrid/PmsGrid.jsx | 22 ++-- src/services/pmsGrid/PmsHeaderOption.jsx | 122 +++++++++++++++--- src/services/pmsGrid/pms-grid.css | 19 ++- src/services/pmsGrid/useGridCore.js | 39 +++++- 7 files changed, 251 insertions(+), 85 deletions(-) diff --git a/src/components/collections/PmGridCollection.jsx b/src/components/collections/PmGridCollection.jsx index 6d7738ca..a88d90a9 100644 --- a/src/components/collections/PmGridCollection.jsx +++ b/src/components/collections/PmGridCollection.jsx @@ -29,7 +29,7 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => { { key: "isActive", title: "Status" }, ]; - const fetcher = async ({ page, pageSize, search }) => { + const fetcher = async ({ page, pageSize, search, filter }) => { const response = await CollectionRepository.getCollections( selectedProject, search || "", @@ -38,7 +38,8 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => { pageSize, page, true, // isActive - isPending + isPending, + filter ); const api = response.data; diff --git a/src/components/common/Chips.jsx b/src/components/common/Chips.jsx index 4b8bd8e3..5a643b83 100644 --- a/src/components/common/Chips.jsx +++ b/src/components/common/Chips.jsx @@ -1,45 +1,68 @@ -import React from 'react' +import React from "react"; -export const EmployeeChip = ({handleRemove,employee}) => { - return( - -
- {employee?.photo ? ( - - {`${employee?.firstName - - ) : ( -
- - {employee?.firstName?.[0] || ""} - {employee?.lastName?.[0] || ""} - -
- )} +export const EmployeeChip = ({ handleRemove, employee }) => { + return ( + +
+ {employee?.photo ? ( + + {`${employee?.firstName + + ) : ( +
+ + {employee?.firstName?.[0] || ""} + {employee?.lastName?.[0] || ""} + +
+ )} -
- - {employee?.firstName} {employee?.lastName} - -
-
+
+ + {employee?.firstName} {employee?.lastName} + +
+
- handleRemove(employee?.id)} - aria-label={`Remove ${employee?.firstName}`} - title="Remove" - /> -
- ) -} + handleRemove(employee?.id)} + aria-label={`Remove ${employee?.firstName}`} + title="Remove" + /> + + ); +}; +export const Chip = ({ handleRemove, chipObj }) => { + return ( + +
+
+ {chipObj?.name} +
+
+ + handleRemove(chipObj?.id)} + aria-label={`Remove ${chipObj.name}`} + title="Remove" + /> +
+ ); +}; diff --git a/src/repositories/ColllectionRepository.jsx b/src/repositories/ColllectionRepository.jsx index 04db4369..343fc5cd 100644 --- a/src/repositories/ColllectionRepository.jsx +++ b/src/repositories/ColllectionRepository.jsx @@ -5,7 +5,7 @@ export const CollectionRepository = { createNewCollection: (data) => api.post(`/api/Collection/invoice/create`, data), updateCollection: (id, data) => { - api.put(`/api/Collection/invoice/edit/${id}`, data) + api.put(`/api/Collection/invoice/edit/${id}`, data); }, // getCollections: (pageSize, pageNumber,fromDate,toDate, isPending,isActive,projectId, searchString) => { // let url = `/api/Collection/invoice/list?pageSize=${pageSize}&pageNumber=${pageNumber}&isPending=${isPending}&isActive=${isActive}&searchString=${searchString}`; @@ -21,7 +21,17 @@ export const CollectionRepository = { // return api.get(url); // }, - getCollections: (projectId, searchString, fromDate, toDate, pageSize, pageNumber, isActive, isPending) => { + getCollections: ( + projectId, + searchString, + fromDate, + toDate, + pageSize, + pageNumber, + isActive, + isPending, + filter + ) => { let url = `/api/Collection/invoice/list`; const params = []; @@ -33,6 +43,7 @@ export const CollectionRepository = { if (pageNumber) params.push(`pageNumber=${pageNumber}`); if (isActive) params.push(`isActive=${isActive}`); if (isPending) params.push(`isPending=${isPending}`); + if (filter) params.push(`filter=${filter}`); if (params.length > 0) { url += "?" + params.join("&"); @@ -41,9 +52,10 @@ export const CollectionRepository = { return api.get(url); }, - makeReceivePayment: (data) => api.post(`/api/Collection/invoice/payment/received`, data), - markPaymentReceived: (invoiceId) => api.put(`/api/Collection/invoice/marked/completed/${invoiceId}`), + makeReceivePayment: (data) => + api.post(`/api/Collection/invoice/payment/received`, data), + markPaymentReceived: (invoiceId) => + api.put(`/api/Collection/invoice/marked/completed/${invoiceId}`), getCollection: (id) => api.get(`/api/Collection/invoice/details/${id}`), - addComment: (data) => api.post(`/api/Collection/invoice/add/comment`, data) + addComment: (data) => api.post(`/api/Collection/invoice/add/comment`, data), }; - diff --git a/src/services/pmsGrid/PmsGrid.jsx b/src/services/pmsGrid/PmsGrid.jsx index 8f5c1adf..be7c4cd7 100644 --- a/src/services/pmsGrid/PmsGrid.jsx +++ b/src/services/pmsGrid/PmsGrid.jsx @@ -38,8 +38,7 @@ export default function PmsGrid({ pageSize, setPage, setPageSize, - setGroupBy -, + setGroupBy, search, setSearch, selected, @@ -181,27 +180,26 @@ export default function PmsGrid({ Group By -
+
{visibleColumns .filter((c) => c.groupable) .map((c) => ( -
grid.setGroupBy(c.key)} - style={{ cursor: "pointer" }} + className="dropdown-item rounded py-1 cursor-pointer" + onClick={() => setGroupBy(c.key)} > {c.title} -
+ ))} {grid.groupBy && ( -
grid.setGroupBy(null)} +
  • setGroupBy(null)} > Clear Grouping -
  • + )}
    diff --git a/src/services/pmsGrid/PmsHeaderOption.jsx b/src/services/pmsGrid/PmsHeaderOption.jsx index 47fce401..7e4d6d24 100644 --- a/src/services/pmsGrid/PmsHeaderOption.jsx +++ b/src/services/pmsGrid/PmsHeaderOption.jsx @@ -1,46 +1,133 @@ +// const PmsHeaderOption = ({ pinned, onPinLeft, onPinRight, onUnpin }) => { +// return ( +//
    +// + +//
      +//
    • +// +//
    • +//
    • +// +//
    • +// {pinned && ( +// <> +//
    • +//
      +//
    • +//
    • +// +//
    • +// +// )} +//
    +//
    +// ); +// }; +// export default PmsHeaderOption; const PmsHeaderOption = ({ pinned, onPinLeft, onPinRight, onUnpin }) => { return ( -
    +
    -
      +
        +
      • + + +
          +
        • + +
        • + +
        • + +
        • +
        +
      • -
      • -
      • -
      • + {pinned && ( <> -

      • +
      • +
        +
      • @@ -49,4 +136,5 @@ const PmsHeaderOption = ({ pinned, onPinLeft, onPinRight, onUnpin }) => {
    ); }; + export default PmsHeaderOption; diff --git a/src/services/pmsGrid/pms-grid.css b/src/services/pmsGrid/pms-grid.css index f918dada..0903a295 100644 --- a/src/services/pmsGrid/pms-grid.css +++ b/src/services/pmsGrid/pms-grid.css @@ -229,4 +229,21 @@ top: 0; margin-left: 0; } -} \ No newline at end of file +} + + + +.dropdown-submenu { + position: relative; +} + +.dropdown-submenu > .dropdown-menu { + top: 0; + left: 100%; + margin-left: 0.1rem; + display: none; +} + +.dropdown-submenu > .dropdown-menu.show { + display: block; +} diff --git a/src/services/pmsGrid/useGridCore.js b/src/services/pmsGrid/useGridCore.js index 58e92f10..6b9ad34f 100644 --- a/src/services/pmsGrid/useGridCore.js +++ b/src/services/pmsGrid/useGridCore.js @@ -19,6 +19,8 @@ export function useGridCore({ const [search, setSearch] = useState(""); const [debouncedSearch, setDebouncedSearch] = useState(""); const [groupBy, setGroupBy] = useState(null); + const [serverGroupRows, setServerGroupRows] = useState(null); + const [sortBy, setSortBy] = useState({ key: null, dir: "asc" }); const [selected, setSelected] = useState(new Set()); const [expanded, setExpanded] = useState(new Set()); @@ -72,6 +74,20 @@ export function useGridCore({ // server-side fetch const fetchServer = useCallback(async () => { + const sortFilters = sortBy.key + ? [ + { + column: sortBy.key, + sortDescending: sortBy.dir === "desc", + }, + ] + : []; + + const filterPayload = JSON.stringify({ + sortFilters, + groupByColumn: groupBy || null, + }); + if (!serverMode || typeof fetcher !== "function") return; setLoading(true); try { @@ -80,6 +96,7 @@ export function useGridCore({ pageSize, sortBy, search: debouncedSearch, + filter: filterPayload, }); // expected: { rows: [], total } setServerRows(resp.rows || []); @@ -87,7 +104,7 @@ export function useGridCore({ } finally { setLoading(false); } - }, [serverMode, fetcher, page, pageSize, sortBy, debouncedSearch]); + }, [serverMode, fetcher, page, pageSize, sortBy, debouncedSearch, groupBy]); useEffect(() => { if (serverMode) fetchServer(); @@ -133,11 +150,21 @@ export function useGridCore({ }, []); const changeSort = useCallback((key) => { - setSortBy((prev) => - prev.key === key - ? { key, dir: prev.dir === "asc" ? "desc" : "asc" } - : { key, dir: "asc" } - ); + setSortBy((prev) => { + // first click = asc + if (prev.key !== key) { + return { key, dir: "asc" }; + } + + // second click = desc + if (prev.dir === "asc") { + return { key, dir: "desc" }; + } + + // third click = remove sort + return { key: null, dir: "asc" }; + }); + setPage(1); }, []);