fixed PmsGrid header
This commit is contained in:
parent
7329319417
commit
73f437e911
18
public/assets/vendor/css/core.css
vendored
18
public/assets/vendor/css/core.css
vendored
@ -480,6 +480,24 @@ th {
|
|||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
}
|
}
|
||||||
|
.vs-th {
|
||||||
|
position: relative;
|
||||||
|
border: none;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.vs-th::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 6px;
|
||||||
|
bottom: 6px;
|
||||||
|
width: 1px;
|
||||||
|
background-color: #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
@ -26,14 +26,18 @@ import { useNavigate } from "react-router-dom";
|
|||||||
const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
|
const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
|
||||||
const [deletingId, setDeletingId] = useState(null);
|
const [deletingId, setDeletingId] = useState(null);
|
||||||
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
const { setViewExpense, setManageExpenseModal, filterData, removeFilterChip } = useExpenseContext();
|
const {
|
||||||
|
setViewExpense,
|
||||||
|
setManageExpenseModal,
|
||||||
|
filterData,
|
||||||
|
removeFilterChip,
|
||||||
|
} = useExpenseContext();
|
||||||
const IsExpenseEditable = useHasUserPermission();
|
const IsExpenseEditable = useHasUserPermission();
|
||||||
const IsExpesneApprpve = useHasUserPermission(APPROVE_EXPENSE);
|
const IsExpesneApprpve = useHasUserPermission(APPROVE_EXPENSE);
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const debouncedSearch = useDebounce(searchText, 500);
|
const debouncedSearch = useDebounce(searchText, 500);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
|
||||||
const { mutate: DeleteExpense, isPending } = useDeleteExpense();
|
const { mutate: DeleteExpense, isPending } = useDeleteExpense();
|
||||||
const { data, isLoading, isError, isInitialLoading, error } = useExpenseList(
|
const { data, isLoading, isError, isInitialLoading, error } = useExpenseList(
|
||||||
ITEMS_PER_PAGE,
|
ITEMS_PER_PAGE,
|
||||||
@ -80,8 +84,9 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
|
|||||||
displayField = "Status";
|
displayField = "Status";
|
||||||
break;
|
break;
|
||||||
case "submittedBy":
|
case "submittedBy":
|
||||||
key = `${item?.createdBy?.firstName ?? ""} ${item.createdBy?.lastName ?? ""
|
key = `${item?.createdBy?.firstName ?? ""} ${
|
||||||
}`.trim();
|
item.createdBy?.lastName ?? ""
|
||||||
|
}`.trim();
|
||||||
displayField = "Submitted By";
|
displayField = "Submitted By";
|
||||||
break;
|
break;
|
||||||
case "project":
|
case "project":
|
||||||
@ -139,11 +144,14 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
|
|||||||
label: "Submitted By",
|
label: "Submitted By",
|
||||||
align: "text-start",
|
align: "text-start",
|
||||||
getValue: (e) =>
|
getValue: (e) =>
|
||||||
`${e.createdBy?.firstName ?? ""} ${e.createdBy?.lastName ?? ""
|
`${e.createdBy?.firstName ?? ""} ${
|
||||||
}`.trim() || "N/A",
|
e.createdBy?.lastName ?? ""
|
||||||
|
}`.trim() || "N/A",
|
||||||
customRender: (e) => (
|
customRender: (e) => (
|
||||||
<div className="d-flex align-items-center cursor-pointer"
|
<div
|
||||||
onClick={() => navigate(`/employee/${e.createdBy?.id}`)}>
|
className="d-flex align-items-center cursor-pointer"
|
||||||
|
onClick={() => navigate(`/employee/${e.createdBy?.id}`)}
|
||||||
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
size="xs"
|
size="xs"
|
||||||
classAvatar="m-0"
|
classAvatar="m-0"
|
||||||
@ -151,8 +159,9 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
|
|||||||
lastName={e.createdBy?.lastName}
|
lastName={e.createdBy?.lastName}
|
||||||
/>
|
/>
|
||||||
<span className="text-truncate">
|
<span className="text-truncate">
|
||||||
{`${e.createdBy?.firstName ?? ""} ${e.createdBy?.lastName ?? ""
|
{`${e.createdBy?.firstName ?? ""} ${
|
||||||
}`.trim() || "N/A"}
|
e.createdBy?.lastName ?? ""
|
||||||
|
}`.trim() || "N/A"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
@ -176,10 +185,9 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
|
|||||||
align: "text-center",
|
align: "text-center",
|
||||||
getValue: (e) => (
|
getValue: (e) => (
|
||||||
<span
|
<span
|
||||||
className={`badge bg-label-${getColorNameFromHex(e?.status?.color) || "secondary"
|
className={`badge bg-label-${
|
||||||
}`}
|
getColorNameFromHex(e?.status?.color) || "secondary"
|
||||||
className={`badge bg-label-${getColorNameFromHex(e?.status?.color) || "secondary"
|
}`}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{e.status?.name || "Unknown"}
|
{e.status?.name || "Unknown"}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -107,8 +107,8 @@ const router = createBrowserRouter(
|
|||||||
{ path: "/tenant/self", element: <SelfTenantDetails /> },
|
{ path: "/tenant/self", element: <SelfTenantDetails /> },
|
||||||
{ path: "/organizations", element: <OrganizationPage /> },
|
{ path: "/organizations", element: <OrganizationPage /> },
|
||||||
{ path: "/help/support", element: <Support /> },
|
{ path: "/help/support", element: <Support /> },
|
||||||
{ path: "/help/docs", element: <Documentation /> },
|
{ path: "/help/docs", element: <DemoBOQGrid /> },
|
||||||
{ path: "/help/connect", element: <DemoBOQGrid /> },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -190,7 +190,6 @@ const boqColumns = [
|
|||||||
*/
|
*/
|
||||||
export default function DemoBOQGrid() {
|
export default function DemoBOQGrid() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 🔥 Initialize Bootstrap popovers after first render
|
|
||||||
initPopover();
|
initPopover();
|
||||||
}, []);
|
}, []);
|
||||||
const wrapperRef = useRef()
|
const wrapperRef = useRef()
|
||||||
@ -204,15 +203,7 @@ useEffect(() => {
|
|||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<div className="container-fluid py-3">
|
<div className="container-fluid py-3">
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-outline-primary"
|
|
||||||
data-bs-toggle="popover"
|
|
||||||
data-bs-placement="right"
|
|
||||||
data-bs-content="This is a popover via CDN!"
|
|
||||||
>
|
|
||||||
Click me
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div className="card p-3">
|
<div className="card p-3">
|
||||||
<PmsGrid
|
<PmsGrid
|
||||||
|
|||||||
@ -135,16 +135,20 @@ export default function PmsGrid({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pms-grid">
|
<div className="pms-grid">
|
||||||
<div className="d-flex justify-content-between mb-2">
|
<div className="row mb-2">
|
||||||
<div className="d-flex gap-2 align-items-center">
|
<div className="col-8">
|
||||||
{features.search && (
|
<div className="d-flex flex-row gap-2 gap-2 ">
|
||||||
|
<div>
|
||||||
|
{features.search && (
|
||||||
<input
|
<input
|
||||||
|
type="search"
|
||||||
className="form-control form-control-sm"
|
className="form-control form-control-sm"
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
{features.export && (
|
{features.export && (
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-outline-secondary"
|
className="btn btn-sm btn-outline-secondary"
|
||||||
@ -159,8 +163,9 @@ export default function PmsGrid({
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div className="d-flex gap-2">
|
<div className="col-4 ">
|
||||||
|
<div className="d-flex justify-content-end gap-2">
|
||||||
{features.columnVisibility && (
|
{features.columnVisibility && (
|
||||||
<ColumnVisibilityPanel
|
<ColumnVisibilityPanel
|
||||||
columns={colState}
|
columns={colState}
|
||||||
@ -186,15 +191,16 @@ export default function PmsGrid({
|
|||||||
</select>
|
</select>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
ref={wrapperRef}
|
ref={wrapperRef}
|
||||||
className="grid-wrapper"
|
className="grid-wrapper text-nowrap"
|
||||||
style={{ maxHeight: features.maxHeight || "60vh" }}
|
style={{ maxHeight: features.maxHeight || "60vh" }}
|
||||||
>
|
>
|
||||||
<table className="table table-sm table-bordered mb-0">
|
<table className="table table-sm mb-0">
|
||||||
<thead
|
<thead
|
||||||
className="table-light"
|
className="bg-light-secondary"
|
||||||
style={{ position: "sticky", top: 0, zIndex: 3 }}
|
style={{ position: "sticky", top: 0, zIndex: 3 }}
|
||||||
>
|
>
|
||||||
<tr>
|
<tr>
|
||||||
@ -202,7 +208,7 @@ export default function PmsGrid({
|
|||||||
<th style={{ width: 32 }} className="text-center">
|
<th style={{ width: 32 }} className="text-center">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="form-check-input"
|
className="form-check-input mx-3"
|
||||||
checked={
|
checked={
|
||||||
currentRows.length > 0 &&
|
currentRows.length > 0 &&
|
||||||
currentRows.every((r) => selected.has(r[rowKey]))
|
currentRows.every((r) => selected.has(r[rowKey]))
|
||||||
@ -232,19 +238,25 @@ export default function PmsGrid({
|
|||||||
onDragStart={(e) => onDragStart(e, col.key)}
|
onDragStart={(e) => onDragStart(e, col.key)}
|
||||||
onDragOver={(e) => e.preventDefault()}
|
onDragOver={(e) => e.preventDefault()}
|
||||||
onDrop={(e) => onDrop(e, col.key)}
|
onDrop={(e) => onDrop(e, col.key)}
|
||||||
className={`pms-col-header ${col.pinned ? "pinned" : ""}`}
|
className={`pms-col-header vs-th ${
|
||||||
|
col.pinned ? "pinned" : ""
|
||||||
|
}`}
|
||||||
style={style}
|
style={style}
|
||||||
>
|
>
|
||||||
<div className="d-flex align-items-center justify-content-between">
|
<div className="d-flex align-items-center justify-content-between px-1">
|
||||||
<div
|
<div
|
||||||
onClick={() => col.sortable && changeSort(col.key)}
|
onClick={() => col.sortable && changeSort(col.key)}
|
||||||
style={{ cursor: col.sortable ? "pointer" : "default" }}
|
style={{ cursor: col.sortable ? "pointer" : "default" }}
|
||||||
>
|
>
|
||||||
<strong>{col.title}</strong>
|
<strong>{col.title}</strong>
|
||||||
{sortBy.key === col.key && (
|
{sortBy.key === col.key && (
|
||||||
<small className="ms-2 text-muted">
|
<i
|
||||||
[{sortBy.dir}]
|
className={`bx bx-sm ${
|
||||||
</small>
|
sortBy.dir === "asc"
|
||||||
|
? "bxs-chevron-up"
|
||||||
|
: "bxs-chevron-down"
|
||||||
|
}`}
|
||||||
|
></i>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex align-items-center gap-1">
|
<div className="d-flex align-items-center gap-1">
|
||||||
@ -270,7 +282,7 @@ export default function PmsGrid({
|
|||||||
})}
|
})}
|
||||||
{features.actions && (
|
{features.actions && (
|
||||||
<th
|
<th
|
||||||
className="text-center sticky-action-column"
|
className="text-center sticky-action-column vs-th bg-white"
|
||||||
style={{ position: "sticky", right: 0, zIndex: 5 }}
|
style={{ position: "sticky", right: 0, zIndex: 5 }}
|
||||||
>
|
>
|
||||||
Actions
|
Actions
|
||||||
@ -357,12 +369,12 @@ export default function PmsGrid({
|
|||||||
function renderRow(row) {
|
function renderRow(row) {
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={row[rowKey]}>
|
<React.Fragment key={row[rowKey]}>
|
||||||
<tr>
|
<tr className={`${selected.has(row[rowKey]) ? "bg-light" : ""}`}>
|
||||||
{features.selection && (
|
{features.selection && (
|
||||||
<td className="text-center p-2">
|
<td className="text-center p-2">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="form-check-input"
|
className="form-check-input"
|
||||||
checked={selected.has(row[rowKey])}
|
checked={selected.has(row[rowKey])}
|
||||||
onChange={() => toggleSelect(row[rowKey])}
|
onChange={() => toggleSelect(row[rowKey])}
|
||||||
/>
|
/>
|
||||||
@ -379,13 +391,17 @@ export default function PmsGrid({
|
|||||||
if (col.pinned === "right")
|
if (col.pinned === "right")
|
||||||
style.right = `${getRightOffset(colState, col.key)}px`;
|
style.right = `${getRightOffset(colState, col.key)}px`;
|
||||||
return (
|
return (
|
||||||
<td key={col.key} style={style}>
|
<td
|
||||||
|
key={col.key}
|
||||||
|
style={style}
|
||||||
|
className={col.pinned ? "bg-white" : ""}
|
||||||
|
>
|
||||||
{col.render ? col.render(row) : row[col.key] ?? ""}
|
{col.render ? col.render(row) : row[col.key] ?? ""}
|
||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{features.actions && (
|
{features.actions && (
|
||||||
<td className="text-center sticky-action-column">
|
<td className="text-center sticky-action-column bg-white">
|
||||||
{features.actions(row, toggleExpand)}
|
{features.actions(row, toggleExpand)}
|
||||||
</td>
|
</td>
|
||||||
)}
|
)}
|
||||||
@ -461,3 +477,4 @@ function ColumnVisibilityPanel({ columns, onToggle }) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user