added hook source for data

This commit is contained in:
pramod.mahajan 2025-12-27 00:08:12 +05:30
parent 468797f823
commit 3ab70dc86f
3 changed files with 238 additions and 219 deletions

View File

@ -1,8 +1,9 @@
import React from "react";
import { PmsGrid } from "../../services/pmsGrid";
import React, { useMemo,useEffect } from "react";
import { PmsGrid, useGridCore } from "../../services/pmsGrid";
import { formatUTCToLocalTime } from "../../utils/dateUtils";
import { formatFigure } from "../../utils/appUtils";
import { CollectionRepository } from "../../repositories/ColllectionRepository";
import { useQuery } from "@tanstack/react-query";
const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
const columns = [
@ -49,23 +50,98 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
{ key: "isActive", title: "Status", enableAdvancedFilter: false },
];
const fetcher = async ({ page, pageSize, search, filter }) => {
const response = await CollectionRepository.getCollections(
selectedProject,
search || "",
fromDate,
toDate,
pageSize,
page,
true, // isActive
isPending,
filter
);
// const fetcher = async ({ page, pageSize, search, filter }) => {
// const response = await CollectionRepository.getCollections(
// selectedProject,
// search || "",
// fromDate,
// toDate,
// pageSize,
// page,
// true, // isActive
// isPending,
// filter
// );
const api = response.data;
// const api = response.data;
return {
rows: api.data.map((item) => ({
// 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 ? (
// <span className="badge bg-label-primary">
// <span className="badge badge-dot bg-primary me-1"></span>
// Active
// </span>
// ) : (
// <span className="badge bg-label-danger">
// <span className="badge badge-dot bg-danger me-1"></span>
// In-Active
// </span>
// ),
// })),
// total: api.totalEntities,
// };
// };
function useGridCollectionQuery({
projectId,
fromDate,
toDate,
isPending,
page,
pageSize,
search,
sortBy,
filter,
}) {
return useQuery({
queryKey: [
"collections",
projectId,
page,
pageSize,
search,
sortBy,
filter,
fromDate,
toDate,
isPending,
],
queryFn: async () => {
const res = await CollectionRepository.getCollections(
projectId,
search || "",
fromDate,
toDate,
pageSize,
page,
true,
isPending,
filter
);
const api = res.data;
return {
rows: api.data.map((item) => ({
id: item.id,
invoiceNumber: item.invoiceNumber,
title: item.title,
@ -97,36 +173,106 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
})),
total: api.totalEntities,
};
};
};
},
keepPreviousData: true,
});
}
const grid = useGridCore({
columns,
serverMode: true,
initialPageSize: 25,
});
const filterPayload = useMemo(() => {
return JSON.stringify({
sortFilters: grid.sortBy.key
? [
{
column: grid.sortBy.key,
sortDescending: grid.sortBy.dir === "desc",
},
]
: [],
advanceFilters: Object.values(grid.advanceFilters),
groupByColumn: grid.groupBy,
});
}, [grid.sortBy, grid.advanceFilters, grid.groupBy]);
const { data, isLoading, error } = useGridCollectionQuery({
projectId: selectedProject,
page: grid.page,
pageSize: grid.pageSize,
search: grid.debouncedSearch,
sortBy: grid.sortBy,
filter: filterPayload,
fromDate,
toDate,
isPending,
});
useEffect(() => {
if (data) {
grid.setRows(data.rows);
grid.setTotalRows(data.total);
}
}, [data]);
return (
// <PmsGrid
// columns={columns}
// serverMode
// fetcher={fetcher}
// rowKey="id"
// features={{
// search: true,
// pagination: true,
// pinning: true,
// resizing: true,
// selection: false,
// reorder: true,
// columnVisibility: true,
// pageSizeSelector: true,
// grouping: true,
// groupByKey: "clientSubmitedDate",
// aggregation: true,
// IsNumbering: true,
// actions: [
// {
// label: "Edit",
// icon: "bx-edit ",
// onClick: (row) => console.log("Edit", row, col),
// },
// {
// label: "Delete",
// icon: "bx-trash text-danger",
// onClick: (row) => console.log("Delete", row),
// },
// ],
// }}
// />
<PmsGrid
columns={columns}
serverMode
fetcher={fetcher}
rowKey="id"
grid={grid}
loading={isLoading}
features={{
search: true,
pagination: true,
pinning: true,
resizing: true,
selection: false,
reorder: true,
grouping: true,
columnVisibility: true,
pageSizeSelector: true,
grouping: true,
groupByKey: "clientSubmitedDate",
aggregation: true,
IsNumbering: true,
actions: [
{
label: "Edit",
icon: "bx-edit ",
onClick: (row) => console.log("Edit", row, col),
icon: "bx-edit",
onClick: (row) => console.log("Edit", row),
},
{
label: "Delete",

View File

@ -15,26 +15,16 @@ import FilterApplied from "./FilterApplied";
*/
export default function PmsGrid({
columns = [],
data,
serverMode = false,
fetcher,
grid,
loading = false,
features = {},
rowKey = "id",
isDropdown = false,
features = {},
renderExpanded,
}) {
const [isFullScreen, setFullScreen] = useState(false);
const [activeCell, setActiveCell] = useState(null);
const grid = useGridCore({
data,
serverMode,
fetcher,
rowKey,
initialPageSize: features.pageSize || 25,
columns,
});
const wrapperRef = useRef();
const {
rows,
page,
@ -55,7 +45,6 @@ export default function PmsGrid({
visibleColumns,
colState,
updateColumn,
loading,
error,
totalRows,
expanded,
@ -67,7 +56,6 @@ export default function PmsGrid({
advanceFilters,
setColumnAdvanceFilter,
} = grid;
// --- Pin / Unpin helpers ---
const pinColumn = (key, side) => {
const col = colState.find((c) => c.key === key);
@ -539,7 +527,12 @@ export default function PmsGrid({
col.pinned
? "pinned-left px-3 bg-white pms-grid td pinned"
: "px-3"
} ${activeCell?.rowId == row.id && activeCell.columnKey === col.key ? "grid-cell-active" : ""} cursor-pointer`}
} ${
activeCell?.rowId == row.id &&
activeCell.columnKey === col.key
? "grid-cell-active"
: ""
} cursor-pointer`}
>
{col.render ? col.render(row) : row[col.key] ?? ""}
</td>

View File

@ -13,11 +13,9 @@ import {
- rowKey
- initialPageSize
*/
export function useGridCore({
data,
data = [],
serverMode = false,
fetcher,
rowKey = "id",
initialPageSize = 20,
columns = [],
@ -28,14 +26,15 @@ export function useGridCore({
const [debouncedSearch, setDebouncedSearch] = useState("");
const [groupBy, setGroupBy] = useState(null);
// FIX: store ADVANCED FILTERS PER COLUMN
// { amount: { columnKey, operation, value } }
const [advanceFilters, setAdvanceFilters] = useState({});
const [sortBy, setSortBy] = useState({ key: null, dir: "asc" });
const [selected, setSelected] = useState(new Set());
const [expanded, setExpanded] = useState(new Set());
const [rows, setRows] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [colState, setColState] = useState(() =>
columns.map((c, i) => ({
...c,
@ -44,24 +43,18 @@ export function useGridCore({
}))
);
const [totalRows, setTotalRows] = useState(data ? data.length : 0);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [serverRows, setServerRows] = useState([]);
/* ---------------- SEARCH (DEBOUNCE) ---------------- */
useEffect(() => {
const handler = setTimeout(() => {
const t = setTimeout(() => {
setDebouncedSearch(search);
setPage(1);
}, 500);
return () => clearTimeout(handler);
}, 400);
return () => clearTimeout(t);
}, [search]);
/* ---------------- CLIENT MODE ---------------- */
const clientFiltered = useMemo(() => {
if (!data) return [];
const clientRows = useMemo(() => {
if (serverMode) return [];
let filtered = data;
@ -69,155 +62,51 @@ export function useGridCore({
const q = search.toLowerCase();
filtered = filtered.filter((r) =>
Object.values(r).some((v) =>
String(v ?? "")
.toLowerCase()
.includes(q)
String(v ?? "").toLowerCase().includes(q)
)
);
}
if (sortBy.key) {
const dir = sortBy.dir === "asc" ? 1 : -1;
filtered = [...filtered].sort((a, b) => {
const A = a[sortBy.key];
const B = b[sortBy.key];
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;
return String(A).localeCompare(String(B)) * dir;
});
filtered = [...filtered].sort((a, b) =>
String(a[sortBy.key] ?? "").localeCompare(
String(b[sortBy.key] ?? "")
) * dir
);
}
setTotalRows(filtered.length);
const start = (page - 1) * pageSize;
return filtered.slice(start, start + pageSize);
}, [data, search, sortBy, page, pageSize]);
/* ---------------- SERVER MODE ---------------- */
const fetchServer = useCallback(async () => {
if (!serverMode || typeof fetcher !== "function") return;
const sortFilters = sortBy.key
? [
{
column: sortBy.key,
sortDescending: sortBy.dir === "desc",
},
]
: [];
// convert map → array
const advanceFilterArray = Object.values(advanceFilters);
const filterPayload = JSON.stringify({
sortFilters,
groupByColumn: groupBy || null,
advanceFilters: advanceFilterArray,
});
setLoading(true);
try {
const resp = await fetcher({
page,
pageSize,
sortBy,
search: debouncedSearch,
filter: filterPayload,
});
setServerRows(resp?.rows || []);
setTotalRows(resp?.total ?? resp?.rows?.length ?? 0);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
}, [
serverMode,
fetcher,
page,
pageSize,
sortBy,
debouncedSearch,
groupBy,
advanceFilters,
]);
}, [data, search, sortBy, page, pageSize, serverMode]);
useEffect(() => {
if (serverMode) fetchServer();
}, [serverMode, fetchServer]);
if (!serverMode) setRows(clientRows);
}, [clientRows, serverMode]);
/* ---------------- ADVANCED FILTER API ---------------- */
/* ---------------- ADVANCED FILTER ---------------- */
const setColumnAdvanceFilter = useCallback((column, filter) => {
setAdvanceFilters((prev) => {
if (!filter) {
const copy = { ...prev };
delete copy[column];
return copy;
const c = { ...prev };
delete c[column];
return c;
}
return {
...prev,
[column]: {
column,
...filter,
},
};
return { ...prev, [column]: { column, ...filter } };
});
setPage(1);
}, []);
/* ---------------- SELECTION ---------------- */
const toggleSelect = useCallback((id) => {
setSelected((prev) => {
const s = new Set(prev);
s.has(id) ? s.delete(id) : s.add(id);
return s;
});
}, []);
const selectAllOnPage = useCallback(
(rows) => {
setSelected((prev) => {
const s = new Set(prev);
rows.forEach((r) => s.add(r[rowKey]));
return s;
});
},
[rowKey]
);
const deselectAllOnPage = useCallback(
(rows) => {
setSelected((prev) => {
const s = new Set(prev);
rows.forEach((r) => s.delete(r[rowKey]));
return s;
});
},
[rowKey]
);
/* ---------------- EXPAND ---------------- */
const toggleExpand = useCallback((id) => {
setExpanded((prev) => {
const s = new Set(prev);
s.has(id) ? s.delete(id) : s.add(id);
return s;
});
}, []);
/* ---------------- SORT ---------------- */
const changeSort = useCallback((key) => {
setSortBy((prev) => {
if (prev.key !== key) return { key, dir: "asc" };
if (prev.dir === "asc") return { key, dir: "desc" };
return { key: null, dir: "asc" };
});
setSortBy((p) =>
p.key !== key
? { key, dir: "asc" }
: p.dir === "asc"
? { key, dir: "desc" }
: { key: null, dir: "asc" }
);
setPage(1);
}, []);
@ -233,64 +122,55 @@ export function useGridCore({
);
}, []);
/* ---------------- FINAL ---------------- */
const rows = serverMode ? serverRows : clientFiltered;
const totalPages = Math.max(1, Math.ceil(totalRows / pageSize));
return {
// paging
/* paging */
page,
setPage,
pageSize,
setPageSize,
totalRows,
totalPages,
totalPages: Math.max(1, Math.ceil(totalRows / pageSize)),
// loading & error
loading,
error,
// search
/* search */
search,
setSearch,
debouncedSearch,
// sorting
/* sorting */
sortBy,
changeSort,
// selection
/* grouping */
groupBy,
setGroupBy,
/* advanced filter */
advanceFilters,
setColumnAdvanceFilter,
/* selection */
selected,
setSelected,
toggleSelect,
selectAllOnPage,
deselectAllOnPage,
// expand
expanded,
toggleExpand,
setExpanded,
// columns
/* columns */
colState,
visibleColumns,
updateColumn,
setColState,
// grouping
groupBy,
setGroupBy,
// advanced filter
advanceFilters,
setColumnAdvanceFilter,
// data
/* data */
rows,
setRows,
setTotalRows,
// mode
serverMode,
rowKey,
};
}
export function useDropdownPosition(btnRef, menuRef, isOpen, level = 0) {
const [style, setStyle] = useState({});