diff --git a/src/components/Project/pmsInfrastructure/BuildingTable.jsx b/src/components/Project/pmsInfrastructure/BuildingTable.jsx index cf4e4afa..707f4c47 100644 --- a/src/components/Project/pmsInfrastructure/BuildingTable.jsx +++ b/src/components/Project/pmsInfrastructure/BuildingTable.jsx @@ -6,16 +6,18 @@ import { useProjectInfra } from "../../../hooks/useProjects"; import { PmsGrid } from "../../../services/pmsGrid"; export default function BuildingTable() { - const project = useSelectedProject() - - const {projectInfra} = useProjectInfra(project,null) + const project = useSelectedProject(); + + const { projectInfra } = useProjectInfra(project, null); const columns = [ - { key: "buildingName", title: "Building", sortable: true,}, + { key: "buildingName", title: "Building", sortable: true,className: "text-start", }, { key: "plannedWork", title: "Planned Work" }, { key: "completedWork", title: "Completed Work" }, { key: "percentage", title: "Completion %" }, ]; + + return ( ( - - )} + renderExpanded={(building) => } /> ); } diff --git a/src/components/collections/CollectionList.jsx b/src/components/collections/CollectionList.jsx index bb94130e..bcd455a9 100644 --- a/src/components/collections/CollectionList.jsx +++ b/src/components/collections/CollectionList.jsx @@ -15,6 +15,8 @@ import { useCollectionContext } from "../../pages/collections/CollectionPage"; import { CollectionTableSkeleton } from "./CollectionSkeleton"; import { useSelectedProject } from "../../slices/apiDataManager"; import { useHasUserPermission } from "../../hooks/useHasUserPermission"; +import { PmsGrid } from "../../services/pmsGrid"; +import PmGridCollection from "./PmGridCollection"; const CollectionList = ({ fromDate, toDate, isPending, searchString }) => { const [currentPage, setCurrentPage] = useState(1); @@ -27,17 +29,17 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => { const selectedProject = useSelectedProject(); const searchDebounce = useDebounce(searchString, 500); - - const { data, isLoading, isError, error } = useCollections( - selectedProject, - searchDebounce, - localToUtc(fromDate), - localToUtc(toDate), - ITEMS_PER_PAGE, - currentPage, - true, - isPending - ); + + // const { data, isLoading, isError, error } = useCollections( + // selectedProject, + // searchDebounce, + // localToUtc(fromDate), + // localToUtc(toDate), + // ITEMS_PER_PAGE, + // currentPage, + // true, + // isPending + // ); const { setProcessedPayment, setAddPayment, setViewCollection } = useCollectionContext(); @@ -147,149 +149,152 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => { }, ]; - if (isLoading) return ; - if (isError) return

{error.message}

; + // if (isLoading) return ; + // if (isError) return

{error.message}

; return ( -
-
-
- - - - {collectionColumns.map((col) => ( - - ))} - {(isAdmin || - canAddPayment || - canViewCollection || - canEditCollection || - canCreate) && } - - - - {Array.isArray(data?.data) && data.data.length > 0 ? ( - data.data.map((row, i) => ( - - {collectionColumns.map((col) => ( - - ))} - {(isAdmin || - canAddPayment || - canViewCollection || - canEditCollection || - canCreate) && ( - + // )} + // + // )) + // ) : ( + // + // + // + // )} + // + //
- {col.label} - Action
- {col.getValue(row)} - -
- + //
+ //
+ //
+ // + // + // + // {collectionColumns.map((col) => ( + // + // ))} + // {(isAdmin || + // canAddPayment || + // canViewCollection || + // canEditCollection || + // canCreate) && } + // + // + // + // {Array.isArray(data?.data) && data.data.length > 0 ? ( + // data.data.map((row, i) => ( + // + // {collectionColumns.map((col) => ( + // + // ))} + // {(isAdmin || + // canAddPayment || + // canViewCollection || + // canEditCollection || + // canCreate) && ( + // - )} - - )) - ) : ( - - - - )} - -
+ // {col.label} + // Action
+ // {col.getValue(row)} + // + //
+ // -
-
- No Collections Found -
- {data?.data?.length > 0 && ( -
- -
- )} -
-
-
+ // {/* Mark Payment */} + // {isAdmin && ( + //
  • + // + // setProcessedPayment({ + // isOpen: true, + // invoiceId: row.id, + // }) + // } + // > + // + // Mark Payment + // + //
  • + // )} + // + // )} + // + //
    + //
    + // No Collections Found + //
    + // {data?.data?.length > 0 && ( + //
    + // + //
    + // )} + //
    + //
    + //
    +
    + +
    ); }; diff --git a/src/components/collections/PmGridCollection.jsx b/src/components/collections/PmGridCollection.jsx new file mode 100644 index 00000000..d6767e9c --- /dev/null +++ b/src/components/collections/PmGridCollection.jsx @@ -0,0 +1,95 @@ +import React from "react"; +import { PmsGrid } from "../../services/pmsGrid"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; +import { formatFigure } from "../../utils/appUtils"; +import { CollectionRepository } from "../../repositories/ColllectionRepository"; + +const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => { + const columns = [ + { key: "invoiceNumber", title: "Invoice Number", className: "text-start" }, + { key: "title", title: "Title", sortable: true, className: "text-start" }, + { + key: "clientSubmitedDate", + title: "Submission Date", + className: "text-start", + }, + { + key: "exceptedPaymentDate", + title: "Expected Payment Date", + className: "text-start", + }, + { key: "totalAmount", title: "Total Amount" }, + { key: "balanceAmount", title: "Balance" }, + { key: "isActive", title: "Status" }, + ]; + + // --- SERVER SIDE FETCHER (correct) --- + const fetcher = async ({ page, pageSize, search }) => { + const response = await CollectionRepository.getCollections( + selectedProject, + search || "", + fromDate, + toDate, + pageSize, + page, + true, // isActive + isPending + ); + + const api = response.data; + + return { + rows: api.data.map((item) => ({ + id: item.id, + invoiceNumber: item.invoiceNumber, + title: item.title, + + clientSubmitedDate: formatUTCToLocalTime(item.clientSubmitedDate), + exceptedPaymentDate: formatUTCToLocalTime(item.exceptedPaymentDate), + + totalAmount: formatFigure(item.basicAmount + item.taxAmount, { + type: "currency", + currency: "INR", + }), + + balanceAmount: formatFigure(item.balanceAmount, { + type: "currency", + currency: "INR", + }), + + isActive: item.isActive ? ( + + + Active + + ) : ( + + + In-Active + + ), + })), + + // MUST use totalRows only + total: api.totalEntities, + }; + }; + + return ( + + ); +}; + +export default PmGridCollection; diff --git a/src/router/AppRoutes.jsx b/src/router/AppRoutes.jsx index 762264ca..9edb83b5 100644 --- a/src/router/AppRoutes.jsx +++ b/src/router/AppRoutes.jsx @@ -61,6 +61,7 @@ import RecurringExpensePage from "../pages/RecurringExpense/RecurringExpensePage import AdvancePaymentPage from "../pages/AdvancePayment/AdvancePaymentPage"; import ServiceProjectDetail from "../pages/ServiceProject/ServiceProjectDetail"; import ManageJob from "../components/ServiceProject/ManageJob"; +import DemoBOQGrid from "../services/pmsGrid/BasicTable"; const router = createBrowserRouter( [ { diff --git a/src/services/pmsGrid/BasicTable.jsx b/src/services/pmsGrid/BasicTable.jsx index 550f35eb..243296e3 100644 --- a/src/services/pmsGrid/BasicTable.jsx +++ b/src/services/pmsGrid/BasicTable.jsx @@ -738,7 +738,7 @@ export default function DemoBOQGrid() { reorder: true, columnVisibility: true, pageSizeSelector: true, - // groupByKey: "status", + groupByKey: "status", aggregation: true, expand: true, maxHeight: "70vh", diff --git a/src/services/pmsGrid/PmsGrid.jsx b/src/services/pmsGrid/PmsGrid.jsx index 2645f042..a28fe118 100644 --- a/src/services/pmsGrid/PmsGrid.jsx +++ b/src/services/pmsGrid/PmsGrid.jsx @@ -170,7 +170,40 @@ export default function PmsGrid({ Export CSV )} - + {features.grouping && ( +
    + + +
    + {visibleColumns + .filter((c) => c.groupable) + .map((c) => ( +
    grid.setGroupBy(c.key)} + style={{ cursor: "pointer" }} + > + {c.title} +
    + ))} + + {grid.groupBy && ( +
    grid.setGroupBy(null)} + > + Clear Grouping +
    + )} +
    +
    + )}
    diff --git a/src/services/pmsGrid/useGridCore.js b/src/services/pmsGrid/useGridCore.js index 291ca769..0ab1915b 100644 --- a/src/services/pmsGrid/useGridCore.js +++ b/src/services/pmsGrid/useGridCore.js @@ -1,4 +1,3 @@ - import { useState, useMemo, useCallback, useEffect } from "react"; /* @@ -7,7 +6,14 @@ import { useState, useMemo, useCallback, useEffect } from "react"; - rowKey - initialPageSize */ -export function useGridCore({ data, serverMode = false, fetcher, rowKey = "id", initialPageSize = 25, columns = [] }) { +export function useGridCore({ + data, + serverMode = false, + fetcher, + rowKey = "id", + initialPageSize = 5, + columns = [], +}) { const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(initialPageSize); const [search, setSearch] = useState(""); @@ -26,9 +32,11 @@ export function useGridCore({ data, serverMode = false, fetcher, rowKey = "id", if (!data) return []; const q = (search || "").toLowerCase(); const filtered = q - ? data.filter(r => - Object.values(r).some(v => - String(v ?? "").toLowerCase().includes(q) + ? data.filter((r) => + Object.values(r).some((v) => + String(v ?? "") + .toLowerCase() + .includes(q) ) ) : data; @@ -41,7 +49,8 @@ export function useGridCore({ data, serverMode = false, fetcher, rowKey = "id", if (A == null && B == null) return 0; if (A == null) return -1 * dir; if (B == null) return 1 * dir; - if (typeof A === "number" && typeof B === "number") return (A - B) * dir; + if (typeof A === "number" && typeof B === "number") + return (A - B) * dir; return String(A).localeCompare(String(B)) * dir; }); } @@ -69,8 +78,8 @@ export function useGridCore({ data, serverMode = false, fetcher, rowKey = "id", }, [serverMode, fetchServer]); // selection - const toggleSelect = useCallback(id => { - setSelected(prev => { + const toggleSelect = useCallback((id) => { + setSelected((prev) => { const s = new Set(prev); s.has(id) ? s.delete(id) : s.add(id); return s; @@ -78,10 +87,10 @@ export function useGridCore({ data, serverMode = false, fetcher, rowKey = "id", }, []); const selectAllOnPage = useCallback( - rows => { - setSelected(prev => { + (rows) => { + setSelected((prev) => { const s = new Set(prev); - rows.forEach(r => s.add(r[rowKey])); + rows.forEach((r) => s.add(r[rowKey])); return s; }); }, @@ -89,26 +98,26 @@ export function useGridCore({ data, serverMode = false, fetcher, rowKey = "id", ); const deselectAllOnPage = useCallback( - rows => { - setSelected(prev => { + (rows) => { + setSelected((prev) => { const s = new Set(prev); - rows.forEach(r => s.delete(r[rowKey])); + rows.forEach((r) => s.delete(r[rowKey])); return s; }); }, [rowKey] ); - const toggleExpand = useCallback(id => { - setExpanded(prev => { + const toggleExpand = useCallback((id) => { + setExpanded((prev) => { const s = new Set(prev); s.has(id) ? s.delete(id) : s.add(id); return s; }); }, []); - const changeSort = useCallback(key => { - setSortBy(prev => + const changeSort = useCallback((key) => { + setSortBy((prev) => prev.key === key ? { key, dir: prev.dir === "asc" ? "desc" : "asc" } : { key, dir: "asc" } @@ -117,7 +126,7 @@ export function useGridCore({ data, serverMode = false, fetcher, rowKey = "id", }, []); const visibleColumns = useMemo( - () => colState.filter(c => c.visible).sort((a, b) => a.order - b.order), + () => colState.filter((c) => c.visible).sort((a, b) => a.order - b.order), [colState] ); @@ -126,7 +135,9 @@ export function useGridCore({ data, serverMode = false, fetcher, rowKey = "id", // update columns externally (reorder/pin/resize) const updateColumn = useCallback((key, patch) => { - setColState(prev => prev.map(c => (c.key === key ? { ...c, ...patch } : c))); + setColState((prev) => + prev.map((c) => (c.key === key ? { ...c, ...patch } : c)) + ); }, []); return {