added filter - sorting, and groupby
This commit is contained in:
parent
b8861fbf41
commit
ee1cf01743
@ -29,7 +29,7 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
|
|||||||
{ key: "isActive", title: "Status" },
|
{ key: "isActive", title: "Status" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const fetcher = async ({ page, pageSize, search }) => {
|
const fetcher = async ({ page, pageSize, search, filter }) => {
|
||||||
const response = await CollectionRepository.getCollections(
|
const response = await CollectionRepository.getCollections(
|
||||||
selectedProject,
|
selectedProject,
|
||||||
search || "",
|
search || "",
|
||||||
@ -38,7 +38,8 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
|
|||||||
pageSize,
|
pageSize,
|
||||||
page,
|
page,
|
||||||
true, // isActive
|
true, // isActive
|
||||||
isPending
|
isPending,
|
||||||
|
filter
|
||||||
);
|
);
|
||||||
|
|
||||||
const api = response.data;
|
const api = response.data;
|
||||||
|
|||||||
@ -1,45 +1,68 @@
|
|||||||
import React from 'react'
|
import React from "react";
|
||||||
|
|
||||||
export const EmployeeChip = ({handleRemove,employee}) => {
|
export const EmployeeChip = ({ handleRemove, employee }) => {
|
||||||
return(
|
return (
|
||||||
<span
|
<span
|
||||||
key={employee?.id}
|
key={employee?.id}
|
||||||
className="tagify__tag d-inline-flex align-items-center me-1 mb-1"
|
className="tagify__tag d-inline-flex align-items-center me-1 mb-1"
|
||||||
role="listitem"
|
role="listitem"
|
||||||
>
|
>
|
||||||
<div className="d-flex align-items-center">
|
<div className="d-flex align-items-center">
|
||||||
{employee?.photo ? (
|
{employee?.photo ? (
|
||||||
<span className="tagify__tag__avatar-wrap me-1">
|
<span className="tagify__tag__avatar-wrap me-1">
|
||||||
<img
|
<img
|
||||||
src={employee?.avataremployeerl || "/defaemployeelt-avatar.png"}
|
src={employee?.avataremployeerl || "/defaemployeelt-avatar.png"}
|
||||||
alt={`${employee?.firstName || ""} ${employee?.lastName || ""}`}
|
alt={`${employee?.firstName || ""} ${employee?.lastName || ""}`}
|
||||||
style={{ width: 12, height: 12, objectFit: "cover" }}
|
style={{ width: 12, height: 12, objectFit: "cover" }}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<div className="avatar avatar-xs me-2">
|
<div className="avatar avatar-xs me-2">
|
||||||
<span className="avatar-initial roemployeended-circle bg-label-secondary">
|
<span className="avatar-initial roemployeended-circle bg-label-secondary">
|
||||||
{employee?.firstName?.[0] || ""}
|
{employee?.firstName?.[0] || ""}
|
||||||
{employee?.lastName?.[0] || ""}
|
{employee?.lastName?.[0] || ""}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="d-flex flex-colemployeemn">
|
<div className="d-flex flex-colemployeemn">
|
||||||
<span className="tagify__tag-text">
|
<span className="tagify__tag-text">
|
||||||
{employee?.firstName} {employee?.lastName}
|
{employee?.firstName} {employee?.lastName}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<bemployeetton
|
<bemployeetton
|
||||||
type="bemployeetton"
|
type="bemployeetton"
|
||||||
className="tagify__tag__removeBtn border-none"
|
className="tagify__tag__removeBtn border-none"
|
||||||
onClick={() => handleRemove(employee?.id)}
|
onClick={() => handleRemove(employee?.id)}
|
||||||
aria-label={`Remove ${employee?.firstName}`}
|
aria-label={`Remove ${employee?.firstName}`}
|
||||||
title="Remove"
|
title="Remove"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export const Chip = ({ handleRemove, chipObj }) => {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
key={chipObj?.id}
|
||||||
|
className="tagify__tag d-inline-flex align-items-center me-1 mb-1"
|
||||||
|
role="listitem"
|
||||||
|
>
|
||||||
|
<div className="d-flex align-items-center">
|
||||||
|
<div className="d-flex flex-colemployeemn">
|
||||||
|
<span className="tagify__tag-text">{chipObj?.name}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<bemployeetton
|
||||||
|
type="bemployeetton"
|
||||||
|
className="tagify__tag__removeBtn border-none"
|
||||||
|
onClick={() => handleRemove(chipObj?.id)}
|
||||||
|
aria-label={`Remove ${chipObj.name}`}
|
||||||
|
title="Remove"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@ -5,7 +5,7 @@ export const CollectionRepository = {
|
|||||||
createNewCollection: (data) =>
|
createNewCollection: (data) =>
|
||||||
api.post(`/api/Collection/invoice/create`, data),
|
api.post(`/api/Collection/invoice/create`, data),
|
||||||
updateCollection: (id, 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) => {
|
// 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}`;
|
// 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);
|
// 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`;
|
let url = `/api/Collection/invoice/list`;
|
||||||
const params = [];
|
const params = [];
|
||||||
|
|
||||||
@ -33,6 +43,7 @@ export const CollectionRepository = {
|
|||||||
if (pageNumber) params.push(`pageNumber=${pageNumber}`);
|
if (pageNumber) params.push(`pageNumber=${pageNumber}`);
|
||||||
if (isActive) params.push(`isActive=${isActive}`);
|
if (isActive) params.push(`isActive=${isActive}`);
|
||||||
if (isPending) params.push(`isPending=${isPending}`);
|
if (isPending) params.push(`isPending=${isPending}`);
|
||||||
|
if (filter) params.push(`filter=${filter}`);
|
||||||
|
|
||||||
if (params.length > 0) {
|
if (params.length > 0) {
|
||||||
url += "?" + params.join("&");
|
url += "?" + params.join("&");
|
||||||
@ -41,9 +52,10 @@ export const CollectionRepository = {
|
|||||||
return api.get(url);
|
return api.get(url);
|
||||||
},
|
},
|
||||||
|
|
||||||
makeReceivePayment: (data) => api.post(`/api/Collection/invoice/payment/received`, data),
|
makeReceivePayment: (data) =>
|
||||||
markPaymentReceived: (invoiceId) => api.put(`/api/Collection/invoice/marked/completed/${invoiceId}`),
|
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}`),
|
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),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -38,8 +38,7 @@ export default function PmsGrid({
|
|||||||
pageSize,
|
pageSize,
|
||||||
setPage,
|
setPage,
|
||||||
setPageSize,
|
setPageSize,
|
||||||
setGroupBy
|
setGroupBy,
|
||||||
,
|
|
||||||
search,
|
search,
|
||||||
setSearch,
|
setSearch,
|
||||||
selected,
|
selected,
|
||||||
@ -181,27 +180,26 @@ export default function PmsGrid({
|
|||||||
Group By
|
Group By
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="dropdown-menu p-2">
|
<div className="dropdown-menu px-1">
|
||||||
{visibleColumns
|
{visibleColumns
|
||||||
.filter((c) => c.groupable)
|
.filter((c) => c.groupable)
|
||||||
.map((c) => (
|
.map((c) => (
|
||||||
<div
|
<li
|
||||||
key={c.key}
|
key={c.key}
|
||||||
className="dropdown-item"
|
className="dropdown-item rounded py-1 cursor-pointer"
|
||||||
onClick={() => grid.setGroupBy(c.key)}
|
onClick={() => setGroupBy(c.key)}
|
||||||
style={{ cursor: "pointer" }}
|
|
||||||
>
|
>
|
||||||
{c.title}
|
{c.title}
|
||||||
</div>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{grid.groupBy && (
|
{grid.groupBy && (
|
||||||
<div
|
<li
|
||||||
className="dropdown-item text-danger"
|
className="dropdown-item text-danger py-1 cursor-pointer rounded"
|
||||||
onClick={() => grid.setGroupBy(null)}
|
onClick={() => setGroupBy(null)}
|
||||||
>
|
>
|
||||||
Clear Grouping
|
Clear Grouping
|
||||||
</div>
|
</li>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,46 +1,133 @@
|
|||||||
|
// const PmsHeaderOption = ({ pinned, onPinLeft, onPinRight, onUnpin }) => {
|
||||||
|
// return (
|
||||||
|
// <div className="dropdown z-100">
|
||||||
|
// <button
|
||||||
|
// type="button"
|
||||||
|
// className="btn btn-icon btn-text-secondary rounded-pill p-0"
|
||||||
|
// data-bs-toggle="dropdown"
|
||||||
|
// data-bs-auto-close="outside"
|
||||||
|
// aria-expanded="true"
|
||||||
|
// >
|
||||||
|
// <i className="bx bx-dots-vertical-rounded bx-sm text-muted"></i>
|
||||||
|
// </button>
|
||||||
|
|
||||||
|
// <ul className="dropdown-menu dropdown-menu-end border shadow rounded-3 py-2">
|
||||||
|
// <li>
|
||||||
|
// <button
|
||||||
|
// className={`dropdown-item d-flex align-items-center ${
|
||||||
|
// pinned === "left" ? "active" : ""
|
||||||
|
// }`}
|
||||||
|
// onClick={onPinLeft}
|
||||||
|
// >
|
||||||
|
// <i className="bx bx-pin me-2"></i> Pin Left
|
||||||
|
// </button>
|
||||||
|
// </li>
|
||||||
|
// <li>
|
||||||
|
// <button
|
||||||
|
// className={`dropdown-item d-flex align-items-center ${
|
||||||
|
// pinned === "right" ? "active" : ""
|
||||||
|
// }`}
|
||||||
|
// onClick={onPinRight}
|
||||||
|
// >
|
||||||
|
// <i className="bx bx-pin me-2"></i> Pin Right
|
||||||
|
// </button>
|
||||||
|
// </li>
|
||||||
|
// {pinned && (
|
||||||
|
// <>
|
||||||
|
// <li>
|
||||||
|
// <hr className="dropdown-divider" />
|
||||||
|
// </li>
|
||||||
|
// <li>
|
||||||
|
// <button
|
||||||
|
// className="dropdown-item text-danger d-flex align-items-center"
|
||||||
|
// onClick={onUnpin}
|
||||||
|
// >
|
||||||
|
// <i className="bx bx-x me-2"></i> Unpin Column
|
||||||
|
// </button>
|
||||||
|
// </li>
|
||||||
|
// </>
|
||||||
|
// )}
|
||||||
|
// </ul>
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
// export default PmsHeaderOption;
|
||||||
const PmsHeaderOption = ({ pinned, onPinLeft, onPinRight, onUnpin }) => {
|
const PmsHeaderOption = ({ pinned, onPinLeft, onPinRight, onUnpin }) => {
|
||||||
return (
|
return (
|
||||||
<div className="dropdown z-100">
|
<div className="dropdown" style={{ zIndex: 9999 }}>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-icon btn-text-secondary rounded-pill p-0"
|
className="btn btn-icon btn-text-secondary rounded-pill p-0"
|
||||||
data-bs-toggle="dropdown"
|
data-bs-toggle="dropdown"
|
||||||
data-bs-auto-close="outside"
|
data-bs-auto-close="outside"
|
||||||
aria-expanded="false"
|
|
||||||
>
|
>
|
||||||
<i className="bx bx-dots-vertical-rounded bx-sm text-muted"></i>
|
<i className="bx bx-dots-vertical-rounded bx-sm text-muted"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<ul className="dropdown-menu dropdown-menu-end shadow-sm border-0 rounded-3 py-2">
|
<ul className="dropdown-menu dropdown-menu-end border shadow rounded-3 py-2">
|
||||||
|
<li className="dropdown-submenu dropstart">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="dropdown-item d-flex align-items-center justify-content-between"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.currentTarget.nextElementSibling.classList.toggle("show");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<i className="bx bx-pin me-2"></i> Pin
|
||||||
|
</span>
|
||||||
|
<i className="bx bx-chevron-right"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<ul className="dropdown-menu border shadow rounded-3 py-2">
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
className={`dropdown-item d-flex align-items-center ${
|
||||||
|
pinned === "left" ? "active" : ""
|
||||||
|
}`}
|
||||||
|
onClick={onPinLeft}
|
||||||
|
>
|
||||||
|
<i className="bx bx-left-arrow-alt me-2"></i>
|
||||||
|
Left
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
className={`dropdown-item d-flex align-items-center ${
|
||||||
|
pinned === "right" ? "active" : ""
|
||||||
|
}`}
|
||||||
|
onClick={onPinRight}
|
||||||
|
>
|
||||||
|
<i className="bx bx-right-arrow-alt me-2"></i>
|
||||||
|
Right
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
className={`dropdown-item d-flex align-items-center ${
|
className={`dropdown-item d-flex align-items-center ${
|
||||||
pinned === "left" ? "active" : ""
|
pinned === "left" ? "active" : ""
|
||||||
}`}
|
}`}
|
||||||
onClick={onPinLeft}
|
|
||||||
>
|
>
|
||||||
<i className="bx bx-pin me-2"></i> Pin Left
|
<i className="bx bx-left-arrow-alt me-2"></i>
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<button
|
|
||||||
className={`dropdown-item d-flex align-items-center ${
|
|
||||||
pinned === "right" ? "active" : ""
|
|
||||||
}`}
|
|
||||||
onClick={onPinRight}
|
|
||||||
>
|
|
||||||
<i className="bx bx-pin me-2"></i> Pin Right
|
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
{pinned && (
|
{pinned && (
|
||||||
<>
|
<>
|
||||||
<li><hr className="dropdown-divider" /></li>
|
<li>
|
||||||
|
<hr className="dropdown-divider" />
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
className="dropdown-item text-danger d-flex align-items-center"
|
className="dropdown-item text-danger d-flex align-items-center"
|
||||||
onClick={onUnpin}
|
onClick={onUnpin}
|
||||||
>
|
>
|
||||||
<i className="bx bx-x me-2"></i> Unpin Column
|
<i className="bx bx-x me-2"></i>
|
||||||
|
Unpin Column
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</>
|
</>
|
||||||
@ -49,4 +136,5 @@ const PmsHeaderOption = ({ pinned, onPinLeft, onPinRight, onUnpin }) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PmsHeaderOption;
|
export default PmsHeaderOption;
|
||||||
|
|||||||
@ -230,3 +230,20 @@
|
|||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|||||||
@ -19,6 +19,8 @@ export function useGridCore({
|
|||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const [debouncedSearch, setDebouncedSearch] = useState("");
|
const [debouncedSearch, setDebouncedSearch] = useState("");
|
||||||
const [groupBy, setGroupBy] = useState(null);
|
const [groupBy, setGroupBy] = useState(null);
|
||||||
|
const [serverGroupRows, setServerGroupRows] = useState(null);
|
||||||
|
|
||||||
const [sortBy, setSortBy] = useState({ key: null, dir: "asc" });
|
const [sortBy, setSortBy] = useState({ key: null, dir: "asc" });
|
||||||
const [selected, setSelected] = useState(new Set());
|
const [selected, setSelected] = useState(new Set());
|
||||||
const [expanded, setExpanded] = useState(new Set());
|
const [expanded, setExpanded] = useState(new Set());
|
||||||
@ -72,6 +74,20 @@ export function useGridCore({
|
|||||||
|
|
||||||
// server-side fetch
|
// server-side fetch
|
||||||
const fetchServer = useCallback(async () => {
|
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;
|
if (!serverMode || typeof fetcher !== "function") return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
@ -80,6 +96,7 @@ export function useGridCore({
|
|||||||
pageSize,
|
pageSize,
|
||||||
sortBy,
|
sortBy,
|
||||||
search: debouncedSearch,
|
search: debouncedSearch,
|
||||||
|
filter: filterPayload,
|
||||||
});
|
});
|
||||||
// expected: { rows: [], total }
|
// expected: { rows: [], total }
|
||||||
setServerRows(resp.rows || []);
|
setServerRows(resp.rows || []);
|
||||||
@ -87,7 +104,7 @@ export function useGridCore({
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [serverMode, fetcher, page, pageSize, sortBy, debouncedSearch]);
|
}, [serverMode, fetcher, page, pageSize, sortBy, debouncedSearch, groupBy]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (serverMode) fetchServer();
|
if (serverMode) fetchServer();
|
||||||
@ -133,11 +150,21 @@ export function useGridCore({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const changeSort = useCallback((key) => {
|
const changeSort = useCallback((key) => {
|
||||||
setSortBy((prev) =>
|
setSortBy((prev) => {
|
||||||
prev.key === key
|
// first click = asc
|
||||||
? { key, dir: prev.dir === "asc" ? "desc" : "asc" }
|
if (prev.key !== key) {
|
||||||
: { key, dir: "asc" }
|
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);
|
setPage(1);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user