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" },
|
||||
];
|
||||
|
||||
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;
|
||||
|
||||
@ -1,45 +1,68 @@
|
||||
import React from 'react'
|
||||
import React from "react";
|
||||
|
||||
export const EmployeeChip = ({handleRemove,employee}) => {
|
||||
return(
|
||||
<span
|
||||
key={employee?.id}
|
||||
className="tagify__tag d-inline-flex align-items-center me-1 mb-1"
|
||||
role="listitem"
|
||||
>
|
||||
<div className="d-flex align-items-center">
|
||||
{employee?.photo ? (
|
||||
<span className="tagify__tag__avatar-wrap me-1">
|
||||
<img
|
||||
src={employee?.avataremployeerl || "/defaemployeelt-avatar.png"}
|
||||
alt={`${employee?.firstName || ""} ${employee?.lastName || ""}`}
|
||||
style={{ width: 12, height: 12, objectFit: "cover" }}
|
||||
/>
|
||||
</span>
|
||||
) : (
|
||||
<div className="avatar avatar-xs me-2">
|
||||
<span className="avatar-initial roemployeended-circle bg-label-secondary">
|
||||
{employee?.firstName?.[0] || ""}
|
||||
{employee?.lastName?.[0] || ""}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
export const EmployeeChip = ({ handleRemove, employee }) => {
|
||||
return (
|
||||
<span
|
||||
key={employee?.id}
|
||||
className="tagify__tag d-inline-flex align-items-center me-1 mb-1"
|
||||
role="listitem"
|
||||
>
|
||||
<div className="d-flex align-items-center">
|
||||
{employee?.photo ? (
|
||||
<span className="tagify__tag__avatar-wrap me-1">
|
||||
<img
|
||||
src={employee?.avataremployeerl || "/defaemployeelt-avatar.png"}
|
||||
alt={`${employee?.firstName || ""} ${employee?.lastName || ""}`}
|
||||
style={{ width: 12, height: 12, objectFit: "cover" }}
|
||||
/>
|
||||
</span>
|
||||
) : (
|
||||
<div className="avatar avatar-xs me-2">
|
||||
<span className="avatar-initial roemployeended-circle bg-label-secondary">
|
||||
{employee?.firstName?.[0] || ""}
|
||||
{employee?.lastName?.[0] || ""}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="d-flex flex-colemployeemn">
|
||||
<span className="tagify__tag-text">
|
||||
{employee?.firstName} {employee?.lastName}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-colemployeemn">
|
||||
<span className="tagify__tag-text">
|
||||
{employee?.firstName} {employee?.lastName}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<bemployeetton
|
||||
type="bemployeetton"
|
||||
className="tagify__tag__removeBtn border-none"
|
||||
onClick={() => handleRemove(employee?.id)}
|
||||
aria-label={`Remove ${employee?.firstName}`}
|
||||
title="Remove"
|
||||
/>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
<bemployeetton
|
||||
type="bemployeetton"
|
||||
className="tagify__tag__removeBtn border-none"
|
||||
onClick={() => handleRemove(employee?.id)}
|
||||
aria-label={`Remove ${employee?.firstName}`}
|
||||
title="Remove"
|
||||
/>
|
||||
</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) =>
|
||||
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),
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
</button>
|
||||
|
||||
<div className="dropdown-menu p-2">
|
||||
<div className="dropdown-menu px-1">
|
||||
{visibleColumns
|
||||
.filter((c) => c.groupable)
|
||||
.map((c) => (
|
||||
<div
|
||||
<li
|
||||
key={c.key}
|
||||
className="dropdown-item"
|
||||
onClick={() => grid.setGroupBy(c.key)}
|
||||
style={{ cursor: "pointer" }}
|
||||
className="dropdown-item rounded py-1 cursor-pointer"
|
||||
onClick={() => setGroupBy(c.key)}
|
||||
>
|
||||
{c.title}
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
|
||||
{grid.groupBy && (
|
||||
<div
|
||||
className="dropdown-item text-danger"
|
||||
onClick={() => grid.setGroupBy(null)}
|
||||
<li
|
||||
className="dropdown-item text-danger py-1 cursor-pointer rounded"
|
||||
onClick={() => setGroupBy(null)}
|
||||
>
|
||||
Clear Grouping
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
</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 }) => {
|
||||
return (
|
||||
<div className="dropdown z-100">
|
||||
<div className="dropdown" style={{ zIndex: 9999 }}>
|
||||
<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="false"
|
||||
>
|
||||
<i className="bx bx-dots-vertical-rounded bx-sm text-muted"></i>
|
||||
</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>
|
||||
<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
|
||||
<i className="bx bx-left-arrow-alt me-2"></i>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
{pinned && (
|
||||
<>
|
||||
<li><hr className="dropdown-divider" /></li>
|
||||
<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
|
||||
<i className="bx bx-x me-2"></i>
|
||||
Unpin Column
|
||||
</button>
|
||||
</li>
|
||||
</>
|
||||
@ -49,4 +136,5 @@ const PmsHeaderOption = ({ pinned, onPinLeft, onPinRight, onUnpin }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PmsHeaderOption;
|
||||
|
||||
@ -229,4 +229,21 @@
|
||||
top: 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 [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);
|
||||
}, []);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user