fixed static data sorting (client side)

This commit is contained in:
pramod.mahajan 2025-12-28 12:53:33 +05:30
parent 2ceb38b496
commit 5a182c5027
5 changed files with 128 additions and 122 deletions

View File

@ -12,6 +12,7 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
title: "Invoice Number", title: "Invoice Number",
className: "text-start", className: "text-start",
groupable: true, groupable: true,
sortable: true,
enableAdvancedFilter: false, enableAdvancedFilter: false,
onCellClick: (row, column) => { onCellClick: (row, column) => {
console.log("Clicked cell:", row, column); console.log("Clicked cell:", row, column);
@ -35,6 +36,7 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
key: "exceptedPaymentDate", key: "exceptedPaymentDate",
title: "Expected Payment Date", title: "Expected Payment Date",
className: "text-start", className: "text-start",
sortable: true,
enableAdvancedFilter: { type: "date" }, enableAdvancedFilter: { type: "date" },
}, },
{ {
@ -148,6 +150,7 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
}); });
}, [grid.sortBy, grid.advanceFilters, grid.groupBy]); }, [grid.sortBy, grid.advanceFilters, grid.groupBy]);
const { data, isLoading, error } = useGridCollectionQuery({ const { data, isLoading, error } = useGridCollectionQuery({
projectId: selectedProject, projectId: selectedProject,
page: grid.page, page: grid.page,
@ -161,9 +164,10 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
}); });
useEffect(() => { useEffect(() => {
if(!data) return
if (data) { if (data) {
grid.setRows(data.rows); grid.setServerRows(data.rows);
grid.setTotalRows(data.total); grid.setServerTotal(data.total);
} }
}, [data]); }, [data]);
@ -174,6 +178,7 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
loading={isLoading} loading={isLoading}
features={{ features={{
search: true, search: true,
selection:true,
pagination: true, pagination: true,
pinning: true, pinning: true,
resizing: true, resizing: true,

View File

@ -621,6 +621,9 @@ const boqColumns = [
sortable: true, sortable: true,
pinned: "left", pinned: "left",
className: "text-start", className: "text-start",
onCellClick: (row, column) => {
console.log("Clicked cell:", row, column);
},
}, },
{ {
key: "description", key: "description",
@ -714,28 +717,15 @@ export default function DemoBOQGrid() {
amount: r.quantity * r.rate, amount: r.quantity * r.rate,
})) }))
}); });
useEffect(() => {
if (!boqData || boqData.length === 0) return;
grid.setRows(
boqData.map((r) => ({
...r,
amount: r.quantity * r.rate,
}))
);
grid.setTotalRows(boqData.length);
}, [boqData]);
return ( return (
<div className="container-fluid py-3"> <div className="container-fluid py-3">
<div className="card p-3"> <div className="card p-3">
<div id="mainContainer" class="container-box"> <div id="mainContainer" className="container-box">
<button id="dropdownBtn" class="btn"> <button id="dropdownBtn" className="btn">
Open Dropdown Open Dropdown
</button> </button>
<div id="dropdownMenu" class="dropdown-menu"> <div id="dropdownMenu" className="dropdown-menu">
<div>Option 1</div> <div>Option 1</div>
<div>Option 2</div> <div>Option 2</div>
<div>Option 3</div> <div>Option 3</div>

View File

@ -357,7 +357,7 @@ export default function PmsGrid({
<tbody> <tbody>
{loading || totalRows === 0 {loading || totalRows === 0
? Array.from({ length: 1 }).map((_, index) => ( ? Array.from({ length: 1 }).map((_, index) => (
<tr className=""> <tr className="" key={`row-${index}`}>
<td <td
key={index} key={index}
colSpan={ colSpan={
@ -390,7 +390,7 @@ export default function PmsGrid({
colSpan={ colSpan={
visibleColumns.length + visibleColumns.length +
(features.selection ? 1 : 0) + (features.selection ? 1 : 0) +
(features.actions ? 1 : 0) (features.actions ? columns.length : 0)
} }
className="text-start pinned pinned-left tr-group" className="text-start pinned pinned-left tr-group"
> >
@ -464,7 +464,7 @@ export default function PmsGrid({
return ( return (
<React.Fragment key={row[rowKey]}> <React.Fragment key={row[rowKey]}>
<tr> <tr >
{features.IsNumbering && ( {features.IsNumbering && (
<td className="text-center align-middle p-2"> <td className="text-center align-middle p-2">
<small className="text-secondry">{ind + 1}</small> <small className="text-secondry">{ind + 1}</small>

View File

@ -64,7 +64,7 @@ const PmsHeaderOption = ({
<div ref={rootRef} style={{ position: "relative", zIndex: 9999 }}> <div ref={rootRef} style={{ position: "relative", zIndex: 9999 }}>
<button <button
ref={btnRef} ref={btnRef}
class="btn btn-icon btn-text-secondary rounded-pill d-inline-block" className="btn btn-icon btn-text-secondary rounded-pill d-inline-block"
onClick={() => setOpen((p) => !p)} onClick={() => setOpen((p) => !p)}
> >
<span className="icon-base bx bx-dots-vertical-rounded bx-sm"></span> <span className="icon-base bx bx-dots-vertical-rounded bx-sm"></span>

View File

@ -13,6 +13,7 @@ import {
- rowKey - rowKey
- initialPageSize - initialPageSize
*/ */
export function useGridCore({ export function useGridCore({
data = [], data = [],
serverMode = false, serverMode = false,
@ -20,6 +21,7 @@ export function useGridCore({
initialPageSize = 20, initialPageSize = 20,
columns = [], columns = [],
}) { }) {
/* ---------------- BASIC STATE ---------------- */
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(initialPageSize); const [pageSize, setPageSize] = useState(initialPageSize);
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
@ -32,9 +34,11 @@ export function useGridCore({
const [selected, setSelected] = useState(new Set()); const [selected, setSelected] = useState(new Set());
const [expanded, setExpanded] = useState(new Set()); const [expanded, setExpanded] = useState(new Set());
const [rows, setRows] = useState([]); /* ---------------- SERVER MODE STATE ---------------- */
const [totalRows, setTotalRows] = useState(0); const [serverRows, setServerRows] = useState([]);
const [serverTotal, setServerTotal] = useState(0);
/* ---------------- COLUMN STATE ---------------- */
const [colState, setColState] = useState(() => const [colState, setColState] = useState(() =>
columns.map((c, i) => ({ columns.map((c, i) => ({
...c, ...c,
@ -43,35 +47,6 @@ export function useGridCore({
})) }))
); );
/* ---------------- 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]
);
/* ---------------- SEARCH (DEBOUNCE) ---------------- */ /* ---------------- SEARCH (DEBOUNCE) ---------------- */
useEffect(() => { useEffect(() => {
const t = setTimeout(() => { const t = setTimeout(() => {
@ -81,47 +56,93 @@ export function useGridCore({
return () => clearTimeout(t); return () => clearTimeout(t);
}, [search]); }, [search]);
/* ---------------- CLIENT MODE ---------------- */ /* ---------------- CLIENT MODE (PURE DERIVED DATA) ---------------- */
const filteredData = useMemo(() => { const filteredData = useMemo(() => {
if (serverMode) return []; if (serverMode) return [];
let filtered = data;
if (search) { let filtered = [...data]; // IMPORTANT: clone
const q = search.toLowerCase();
filtered = filtered.filter((r) => // SEARCH
Object.values(r).some((v) => if (debouncedSearch) {
String(v ?? "").toLowerCase().includes(q) const q = debouncedSearch.toLowerCase();
) filtered = filtered.filter((r) =>
Object.values(r).some((v) =>
String(v ?? "")
.toLowerCase()
.includes(q)
)
);
}
// SORT
if (sortBy.key) {
const dir = sortBy.dir === "asc" ? 1 : -1;
filtered.sort(
(a, b) =>
String(a?.[sortBy.key] ?? "").localeCompare(
String(b?.[sortBy.key] ?? ""),
undefined,
{ numeric: true, sensitivity: "base" }
) * dir
);
}
return filtered;
}, [data, debouncedSearch, sortBy, serverMode]);
const pagedClientRows = useMemo(() => {
if (serverMode) return [];
const start = (page - 1) * pageSize;
return filteredData.slice(start, start + pageSize);
}, [filteredData, page, pageSize, serverMode]);
/* ---------------- FINAL ROW SOURCE ---------------- */
const rows = serverMode ? serverRows : pagedClientRows;
const totalRows = serverMode ? serverTotal : filteredData.length;
const totalPages = Math.max(1, Math.ceil(totalRows / pageSize));
/* ---------------- 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]
);
/* ---------------- SORT ---------------- */
const changeSort = useCallback((key) => {
setSortBy((p) =>
p.key !== key
? { key, dir: "asc" }
: p.dir === "asc"
? { key, dir: "desc" }
: { key: null, dir: "asc" }
); );
} setPage(1);
}, []);
if (sortBy.key) {
const dir = sortBy.dir === "asc" ? 1 : -1;
filtered = [...filtered].sort(
(a, b) =>
String(a[sortBy.key] ?? "").localeCompare(
String(b[sortBy.key] ?? "")
) * dir
);
}
return filtered;
}, [data, search, sortBy, serverMode]);
const pagedRows = useMemo(() => {
const start = (page - 1) * pageSize;
return filteredData.slice(start, start + pageSize);
}, [filteredData, page, pageSize]);
useEffect(() => {
if (!serverMode) {
setRows(pagedRows);
setTotalRows(filteredData.length);
}
}, [serverMode, page, pageSize, filteredData.length]);
/* ---------------- ADVANCED FILTER ---------------- */ /* ---------------- ADVANCED FILTER ---------------- */
const setColumnAdvanceFilter = useCallback((column, filter) => { const setColumnAdvanceFilter = useCallback((column, filter) => {
@ -136,18 +157,6 @@ useEffect(() => {
setPage(1); setPage(1);
}, []); }, []);
/* ---------------- SORT ---------------- */
const changeSort = useCallback((key) => {
setSortBy((p) =>
p.key !== key
? { key, dir: "asc" }
: p.dir === "asc"
? { key, dir: "desc" }
: { key: null, dir: "asc" }
);
setPage(1);
}, []);
/* ---------------- COLUMNS ---------------- */ /* ---------------- COLUMNS ---------------- */
const visibleColumns = useMemo( 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),
@ -160,8 +169,7 @@ useEffect(() => {
); );
}, []); }, []);
// *---------------- ROW EXPAND -----------------------*/ /* ---------------- EXPAND ---------------- */
const toggleExpand = useCallback((id) => { const toggleExpand = useCallback((id) => {
setExpanded((prev) => { setExpanded((prev) => {
const s = new Set(prev); const s = new Set(prev);
@ -169,55 +177,58 @@ useEffect(() => {
return s; return s;
}); });
}, []); }, []);
/* ---------------- RETURN API ---------------- */
return { return {
/* paging */ // paging
page, page,
setPage, setPage,
pageSize, pageSize,
setPageSize, setPageSize,
totalRows, totalRows,
totalPages: Math.max(1, Math.ceil(totalRows / pageSize)), totalPages,
/* search */ // search
search, search,
setSearch, setSearch,
debouncedSearch, debouncedSearch,
/* ----------sorting------ */ // sorting
sortBy, sortBy,
changeSort, changeSort,
/* --------------grouping---------------- */ // grouping
groupBy, groupBy,
setGroupBy, setGroupBy,
/* --------------advanced filter----------------- */ // advanced filters
advanceFilters, advanceFilters,
setColumnAdvanceFilter, setColumnAdvanceFilter,
/*--------- selection ---------------------- */ // selection
selected, selected,
setSelected,
toggleSelect, toggleSelect,
selectAllOnPage, selectAllOnPage,
deselectAllOnPage, deselectAllOnPage,
// *------------- Expanding Row --------------*/ // expand
expanded, expanded,
setExpanded, toggleExpand,
/* --------------columns--------------------- */ // columns
colState, colState,
visibleColumns, visibleColumns,
updateColumn, updateColumn,
setColState, setColState,
toggleExpand,
/* ---------------data---------------------- */ // data
rows, rows,
setRows,
setTotalRows, // server setters (ONLY used in serverMode)
/*------------Key and grid-mode-------------*/ setServerRows,
setServerTotal,
// flags
serverMode, serverMode,
rowKey, rowKey,
}; };