update action btn
This commit is contained in:
parent
73f437e911
commit
e77fbf6d1d
4
public/assets/vendor/css/core.css
vendored
4
public/assets/vendor/css/core.css
vendored
@ -483,7 +483,7 @@ th {
|
||||
.vs-th {
|
||||
position: relative;
|
||||
border: none;
|
||||
background-color: #f8f9fa;
|
||||
background-color: white;
|
||||
padding: 0.75rem 1rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@ -496,7 +496,7 @@ content: "";
|
||||
top: 6px;
|
||||
bottom: 6px;
|
||||
width: 1px;
|
||||
background-color: #dee2e6;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
label {
|
||||
|
||||
4
src/assets/vendor/css/core.css
vendored
4
src/assets/vendor/css/core.css
vendored
@ -16497,6 +16497,10 @@ html:not([dir=rtl]) .toast.bs-toast .toast-header .btn-close {
|
||||
.z-5 {
|
||||
z-index: 5 !important;
|
||||
}
|
||||
.z-6 {
|
||||
z-index: 10 !important;
|
||||
}
|
||||
|
||||
|
||||
.cursor-pointer {
|
||||
cursor: pointer !important;
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import React, { useEffect ,useRef} from "react";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { PmsGrid } from "./index";
|
||||
import { initPopover } from "./GridService";
|
||||
|
||||
|
||||
/**
|
||||
* CIVIL BOQ / INVENTORY DEMO DATA
|
||||
* Each row = BOQ item, grouped by "Category"
|
||||
@ -128,22 +127,527 @@ const boqData = [
|
||||
vendor: "Classic Stone",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
category: "Electrical Works",
|
||||
itemCode: "E-001",
|
||||
description: "PVC Conduit 25mm dia",
|
||||
unit: "Mtr",
|
||||
quantity: 1500,
|
||||
rate: 45,
|
||||
site: "Eco Towers",
|
||||
vendor: "Finolex",
|
||||
status: "In Progress",
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
category: "Electrical Works",
|
||||
itemCode: "E-002",
|
||||
description: "Copper Wire 4 sqmm",
|
||||
unit: "Mtr",
|
||||
quantity: 2000,
|
||||
rate: 65,
|
||||
site: "Skyline Heights",
|
||||
vendor: "Polycab",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
category: "Plumbing Works",
|
||||
itemCode: "PL-001",
|
||||
description: "CPVC Pipe 25mm dia",
|
||||
unit: "Mtr",
|
||||
quantity: 1000,
|
||||
rate: 120,
|
||||
site: "Green City - Tower C",
|
||||
vendor: "Astral Pipes",
|
||||
status: "Pending",
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
category: "Plumbing Works",
|
||||
itemCode: "PL-002",
|
||||
description: "UPVC Pipe 40mm dia",
|
||||
unit: "Mtr",
|
||||
quantity: 800,
|
||||
rate: 100,
|
||||
site: "Sunshine Plaza",
|
||||
vendor: "Supreme Industries",
|
||||
status: "In Progress",
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
category: "Painting Works",
|
||||
itemCode: "PA-001",
|
||||
description: "Interior Emulsion Paint",
|
||||
unit: "Ltr",
|
||||
quantity: 500,
|
||||
rate: 180,
|
||||
site: "Skyline Heights",
|
||||
vendor: "Asian Paints",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
category: "Painting Works",
|
||||
itemCode: "PA-002",
|
||||
description: "Exterior Weatherproof Paint",
|
||||
unit: "Ltr",
|
||||
quantity: 400,
|
||||
rate: 220,
|
||||
site: "Green City - Tower D",
|
||||
vendor: "Berger Paints",
|
||||
status: "Pending",
|
||||
},
|
||||
{
|
||||
id: 17,
|
||||
category: "Waterproofing Works",
|
||||
itemCode: "W-001",
|
||||
description: "Cementitious Coating 2 layers",
|
||||
unit: "Sqm",
|
||||
quantity: 350,
|
||||
rate: 480,
|
||||
site: "Sunshine Plaza",
|
||||
vendor: "Dr. Fixit",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
category: "Waterproofing Works",
|
||||
itemCode: "W-002",
|
||||
description: "Polyurethane Waterproofing Membrane",
|
||||
unit: "Sqm",
|
||||
quantity: 200,
|
||||
rate: 850,
|
||||
site: "Green City - Podium",
|
||||
vendor: "Pidilite",
|
||||
status: "In Progress",
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
category: "HVAC Works",
|
||||
itemCode: "H-001",
|
||||
description: "Ducting for AHU system",
|
||||
unit: "Sqm",
|
||||
quantity: 250,
|
||||
rate: 2100,
|
||||
site: "Skyline Heights - Basement",
|
||||
vendor: "Blue Star",
|
||||
status: "Pending",
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
category: "HVAC Works",
|
||||
itemCode: "H-002",
|
||||
description: "VRF Indoor Unit Installation",
|
||||
unit: "Nos",
|
||||
quantity: 30,
|
||||
rate: 42000,
|
||||
site: "Eco Towers",
|
||||
vendor: "Daikin",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 21,
|
||||
category: "Plumbing Works",
|
||||
itemCode: "PL-003",
|
||||
description: "CP Fittings - Wash Basin Mixer",
|
||||
unit: "Nos",
|
||||
quantity: 50,
|
||||
rate: 950,
|
||||
site: "Skyline Heights",
|
||||
vendor: "Jaquar",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
category: "Electrical Works",
|
||||
itemCode: "E-003",
|
||||
description: "LED Downlight 12W",
|
||||
unit: "Nos",
|
||||
quantity: 120,
|
||||
rate: 750,
|
||||
site: "Green City - Tower B",
|
||||
vendor: "Philips",
|
||||
status: "Pending",
|
||||
},
|
||||
{
|
||||
id: 23,
|
||||
category: "Flooring Works",
|
||||
itemCode: "F-003",
|
||||
description: "Wooden Flooring 12mm laminate",
|
||||
unit: "Sqm",
|
||||
quantity: 150,
|
||||
rate: 1450,
|
||||
site: "Sunshine Plaza - Office",
|
||||
vendor: "Pergo",
|
||||
status: "In Progress",
|
||||
},
|
||||
{
|
||||
id: 24,
|
||||
category: "Concrete Works",
|
||||
itemCode: "C-003",
|
||||
description: "PCC 1:4:8 flooring",
|
||||
unit: "Cum",
|
||||
quantity: 60,
|
||||
rate: 4800,
|
||||
site: "Eco Towers",
|
||||
vendor: "RMC Readymix",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 25,
|
||||
category: "Masonry Works",
|
||||
itemCode: "M-003",
|
||||
description: "Stone masonry in foundation",
|
||||
unit: "Cum",
|
||||
quantity: 45,
|
||||
rate: 3500,
|
||||
site: "Green City - Tower C",
|
||||
vendor: "Shree Bricks",
|
||||
status: "In Progress",
|
||||
},
|
||||
{
|
||||
id: 26,
|
||||
category: "Carpentry Works",
|
||||
itemCode: "CP-001",
|
||||
description: "Flush Door with Laminate Finish",
|
||||
unit: "Nos",
|
||||
quantity: 80,
|
||||
rate: 6500,
|
||||
site: "Skyline Heights",
|
||||
vendor: "Greenply",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 27,
|
||||
category: "Carpentry Works",
|
||||
itemCode: "CP-002",
|
||||
description: "Modular Kitchen Cabinets",
|
||||
unit: "Set",
|
||||
quantity: 25,
|
||||
rate: 42000,
|
||||
site: "Sunshine Plaza",
|
||||
vendor: "Godrej Interio",
|
||||
status: "Pending",
|
||||
},
|
||||
{
|
||||
id: 28,
|
||||
category: "Glazing Works",
|
||||
itemCode: "G-001",
|
||||
description: "Aluminium Window Frame with Glass",
|
||||
unit: "Sqm",
|
||||
quantity: 180,
|
||||
rate: 1850,
|
||||
site: "Green City - Tower D",
|
||||
vendor: "Saint Gobain",
|
||||
status: "In Progress",
|
||||
},
|
||||
{
|
||||
id: 29,
|
||||
category: "Glazing Works",
|
||||
itemCode: "G-002",
|
||||
description: "Toughened Glass Door 12mm",
|
||||
unit: "Sqm",
|
||||
quantity: 60,
|
||||
rate: 2750,
|
||||
site: "Skyline Heights - Lobby",
|
||||
vendor: "Modiguard",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 30,
|
||||
category: "External Development",
|
||||
itemCode: "ED-001",
|
||||
description: "Paver Block 60mm thick",
|
||||
unit: "Sqm",
|
||||
quantity: 950,
|
||||
rate: 380,
|
||||
site: "Sunshine Plaza",
|
||||
vendor: "Ultra Pavers",
|
||||
status: "Pending",
|
||||
},
|
||||
{
|
||||
id: 31,
|
||||
category: "External Development",
|
||||
itemCode: "ED-002",
|
||||
description: "Kerb Stone 300x300mm",
|
||||
unit: "Rm",
|
||||
quantity: 200,
|
||||
rate: 240,
|
||||
site: "Green City - Parking Area",
|
||||
vendor: "L&T Infra",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 32,
|
||||
category: "Electrical Works",
|
||||
itemCode: "E-004",
|
||||
description: "Distribution Board with MCB",
|
||||
unit: "Nos",
|
||||
quantity: 25,
|
||||
rate: 6200,
|
||||
site: "Skyline Heights",
|
||||
vendor: "Legrand",
|
||||
status: "In Progress",
|
||||
},
|
||||
{
|
||||
id: 33,
|
||||
category: "Plastering Works",
|
||||
itemCode: "P-003",
|
||||
description: "Ceiling POP Finish",
|
||||
unit: "Sqm",
|
||||
quantity: 950,
|
||||
rate: 210,
|
||||
site: "Green City - Tower A",
|
||||
vendor: "L&T Finishes",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 34,
|
||||
category: "Painting Works",
|
||||
itemCode: "PA-003",
|
||||
description: "Metal Primer + Enamel Paint",
|
||||
unit: "Ltr",
|
||||
quantity: 350,
|
||||
rate: 160,
|
||||
site: "Sunshine Plaza - Basement",
|
||||
vendor: "Nerolac Paints",
|
||||
status: "In Progress",
|
||||
},
|
||||
{
|
||||
id: 35,
|
||||
category: "HVAC Works",
|
||||
itemCode: "H-003",
|
||||
description: "Duct Insulation 50mm thick",
|
||||
unit: "Sqm",
|
||||
quantity: 120,
|
||||
rate: 540,
|
||||
site: "Eco Towers",
|
||||
vendor: "Blue Star",
|
||||
status: "Pending",
|
||||
},
|
||||
{
|
||||
id: 36,
|
||||
category: "Plumbing Works",
|
||||
itemCode: "PL-004",
|
||||
description: "Overhead Water Tank Installation",
|
||||
unit: "Nos",
|
||||
quantity: 3,
|
||||
rate: 45000,
|
||||
site: "Green City - Tower B",
|
||||
vendor: "Sintex",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 37,
|
||||
category: "Electrical Works",
|
||||
itemCode: "E-005",
|
||||
description: "Earthing Pit with GI Plate",
|
||||
unit: "Nos",
|
||||
quantity: 6,
|
||||
rate: 9500,
|
||||
site: "Skyline Heights",
|
||||
vendor: "KEI",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 38,
|
||||
category: "Concrete Works",
|
||||
itemCode: "C-004",
|
||||
description: "M30 Concrete for beams",
|
||||
unit: "Cum",
|
||||
quantity: 95,
|
||||
rate: 5800,
|
||||
site: "Eco Towers - Basement",
|
||||
vendor: "RMC Readymix",
|
||||
status: "In Progress",
|
||||
},
|
||||
{
|
||||
id: 39,
|
||||
category: "Masonry Works",
|
||||
itemCode: "M-004",
|
||||
description: "Cement Mortar 1:4 plaster backing",
|
||||
unit: "Sqm",
|
||||
quantity: 700,
|
||||
rate: 180,
|
||||
site: "Skyline Heights - Wing E",
|
||||
vendor: "Ambuja Blocks",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 40,
|
||||
category: "Flooring Works",
|
||||
itemCode: "F-004",
|
||||
description: "Marble Flooring 20mm thick",
|
||||
unit: "Sqm",
|
||||
quantity: 260,
|
||||
rate: 2100,
|
||||
site: "Green City - Tower D",
|
||||
vendor: "Classic Stone",
|
||||
status: "Pending",
|
||||
},
|
||||
{
|
||||
id: 41,
|
||||
category: "Carpentry Works",
|
||||
itemCode: "CP-003",
|
||||
description: "Wardrobe with Laminate Finish",
|
||||
unit: "Nos",
|
||||
quantity: 40,
|
||||
rate: 18500,
|
||||
site: "Sunshine Plaza",
|
||||
vendor: "Durian",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 42,
|
||||
category: "Glazing Works",
|
||||
itemCode: "G-003",
|
||||
description: "Spider Glass Façade System",
|
||||
unit: "Sqm",
|
||||
quantity: 150,
|
||||
rate: 3550,
|
||||
site: "Eco Towers",
|
||||
vendor: "Saint Gobain",
|
||||
status: "In Progress",
|
||||
},
|
||||
{
|
||||
id: 43,
|
||||
category: "Waterproofing Works",
|
||||
itemCode: "W-003",
|
||||
description: "Bituminous Membrane Layer 3mm",
|
||||
unit: "Sqm",
|
||||
quantity: 400,
|
||||
rate: 620,
|
||||
site: "Skyline Heights - Terrace",
|
||||
vendor: "Dr. Fixit",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 44,
|
||||
category: "External Development",
|
||||
itemCode: "ED-003",
|
||||
description: "RCC Drain Construction",
|
||||
unit: "Rm",
|
||||
quantity: 180,
|
||||
rate: 950,
|
||||
site: "Green City - Service Road",
|
||||
vendor: "L&T Infra",
|
||||
status: "In Progress",
|
||||
},
|
||||
{
|
||||
id: 45,
|
||||
category: "Electrical Works",
|
||||
itemCode: "E-006",
|
||||
description: "Street Light Pole 9m",
|
||||
unit: "Nos",
|
||||
quantity: 25,
|
||||
rate: 18500,
|
||||
site: "Sunshine Plaza - Entry Road",
|
||||
vendor: "Polycab",
|
||||
status: "Pending",
|
||||
},
|
||||
{
|
||||
id: 46,
|
||||
category: "Painting Works",
|
||||
itemCode: "PA-004",
|
||||
description: "Texture Finish Coating",
|
||||
unit: "Sqm",
|
||||
quantity: 320,
|
||||
rate: 480,
|
||||
site: "Green City - Clubhouse",
|
||||
vendor: "Asian Paints",
|
||||
status: "In Progress",
|
||||
},
|
||||
{
|
||||
id: 47,
|
||||
category: "Flooring Works",
|
||||
itemCode: "F-005",
|
||||
description: "Epoxy Flooring 3mm thick",
|
||||
unit: "Sqm",
|
||||
quantity: 150,
|
||||
rate: 850,
|
||||
site: "Skyline Heights - Basement",
|
||||
vendor: "Pidilite",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 48,
|
||||
category: "Plumbing Works",
|
||||
itemCode: "PL-005",
|
||||
description: "Bathroom CP fittings set",
|
||||
unit: "Set",
|
||||
quantity: 40,
|
||||
rate: 5400,
|
||||
site: "Eco Towers",
|
||||
vendor: "Hindware",
|
||||
status: "Completed",
|
||||
},
|
||||
{
|
||||
id: 49,
|
||||
category: "Concrete Works",
|
||||
itemCode: "C-005",
|
||||
description: "Ready Mix M40 Concrete",
|
||||
unit: "Cum",
|
||||
quantity: 60,
|
||||
rate: 6100,
|
||||
site: "Green City - Tower E",
|
||||
vendor: "ACC",
|
||||
status: "Pending",
|
||||
},
|
||||
{
|
||||
id: 50,
|
||||
category: "External Development",
|
||||
itemCode: "ED-004",
|
||||
description: "Compound Wall RCC Precast",
|
||||
unit: "Rm",
|
||||
quantity: 300,
|
||||
rate: 1750,
|
||||
site: "Sunshine Plaza - Boundary",
|
||||
vendor: "UltraTech",
|
||||
status: "Completed",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* COLUMN DEFINITIONS
|
||||
*/
|
||||
const boqColumns = [
|
||||
{ key: "itemCode", title: "Item Code", sortable: true, pinned: "left" },
|
||||
{ key: "description", title: "Description", sortable: true, width: 300 },
|
||||
{ key: "category", title: "Category", sortable: true },
|
||||
{ key: "unit", title: "Unit", width: 80 },
|
||||
{ key: "quantity", title: "Qty", sortable: true, width: 100 },
|
||||
{
|
||||
key: "itemCode",
|
||||
title: "Item Code",
|
||||
sortable: true,
|
||||
pinned: "left",
|
||||
className: "text-start",
|
||||
},
|
||||
{
|
||||
key: "description",
|
||||
title: "Description",
|
||||
sortable: true,
|
||||
width: 300,
|
||||
className: "text-start",
|
||||
},
|
||||
{
|
||||
key: "category",
|
||||
title: "Category",
|
||||
sortable: true,
|
||||
className: "text-start",
|
||||
},
|
||||
{ key: "unit", title: "Unit", width: 80, className: "text-ceter" },
|
||||
{
|
||||
key: "quantity",
|
||||
title: "Qty",
|
||||
sortable: true,
|
||||
width: 100,
|
||||
className: "text-cnter px-2",
|
||||
},
|
||||
{
|
||||
key: "rate",
|
||||
title: "Rate (₹)",
|
||||
sortable: true,
|
||||
width: 120,
|
||||
className: "text-end",
|
||||
render: (r) => <span>₹{r.rate.toLocaleString()}</span>,
|
||||
},
|
||||
{
|
||||
@ -151,27 +655,38 @@ const boqColumns = [
|
||||
title: "Amount (₹)",
|
||||
sortable: true,
|
||||
width: 130,
|
||||
className: "text-end",
|
||||
render: (r) => (
|
||||
<span className="fw-semibold text-success">
|
||||
<span className="fw-semibold text-end px-2 py-12">
|
||||
₹{(r.quantity * r.rate).toLocaleString()}
|
||||
</span>
|
||||
),
|
||||
aggregate: (vals) =>
|
||||
"₹" +
|
||||
vals
|
||||
.reduce((sum, val) => sum + val, 0)
|
||||
.toLocaleString(),
|
||||
"₹" + vals.reduce((sum, val) => sum + val, 0).toLocaleString(),
|
||||
},
|
||||
{
|
||||
key: "vendor",
|
||||
title: "Vendor",
|
||||
sortable: true,
|
||||
width: 180,
|
||||
className: "text-start",
|
||||
},
|
||||
{
|
||||
key: "site",
|
||||
title: "Site Location",
|
||||
sortable: true,
|
||||
width: 200,
|
||||
className: "text-start",
|
||||
},
|
||||
{ key: "vendor", title: "Vendor", sortable: true, width: 180 },
|
||||
{ key: "site", title: "Site Location", sortable: true, width: 200 },
|
||||
{
|
||||
key: "status",
|
||||
title: "Status",
|
||||
sortable: true,
|
||||
width: 120,
|
||||
className: "text-center",
|
||||
render: (r) => (
|
||||
<span
|
||||
className={`badge bg-${
|
||||
className={`badge bg-label-${
|
||||
r.status === "Completed"
|
||||
? "success"
|
||||
: r.status === "Pending"
|
||||
@ -189,22 +704,20 @@ const boqColumns = [
|
||||
* DEMO COMPONENT
|
||||
*/
|
||||
export default function DemoBOQGrid() {
|
||||
useEffect(() => {
|
||||
useEffect(() => {
|
||||
initPopover();
|
||||
}, []);
|
||||
const wrapperRef = useRef()
|
||||
useEffect(() => {
|
||||
if (!wrapperRef.current) return;
|
||||
const ps = new PerfectScrollbar(wrapperRef.current, {
|
||||
wheelPropagation: false,
|
||||
suppressScrollX: false,
|
||||
});
|
||||
return () => ps.destroy();
|
||||
}, []);
|
||||
const wrapperRef = useRef();
|
||||
useEffect(() => {
|
||||
if (!wrapperRef.current) return;
|
||||
const ps = new PerfectScrollbar(wrapperRef.current, {
|
||||
wheelPropagation: false,
|
||||
suppressScrollX: false,
|
||||
});
|
||||
return () => ps.destroy();
|
||||
}, []);
|
||||
return (
|
||||
<div className="container-fluid py-3">
|
||||
|
||||
|
||||
<div className="card p-3">
|
||||
<PmsGrid
|
||||
data={boqData.map((r) => ({
|
||||
@ -225,19 +738,24 @@ useEffect(() => {
|
||||
// groupByKey: "category",
|
||||
aggregation: true,
|
||||
expand: true,
|
||||
maxHeight: "60vh",
|
||||
actions: (row, toggleExpand) => (
|
||||
<>
|
||||
<button
|
||||
className="btn btn-sm btn-outline-primary"data-bs-toggle="popover" data-bs-content="Popover text"
|
||||
onClick={() => toggleExpand(row.id)}
|
||||
>
|
||||
<i className="bx bx-detail me-1"></i>
|
||||
Details
|
||||
|
||||
</button>
|
||||
</>
|
||||
),
|
||||
maxHeight: "70vh",
|
||||
actions: [
|
||||
{
|
||||
label: "Edit",
|
||||
icon: "bx-edit ",
|
||||
onClick: (row) => console.log("Edit", row),
|
||||
},
|
||||
{
|
||||
label: "Delete",
|
||||
icon: "bx-trash text-danger",
|
||||
onClick: (row) => console.log("Delete", row),
|
||||
},
|
||||
{
|
||||
label: "View",
|
||||
icon: "bx-show text-info",
|
||||
onClick: (row) => console.log("View ", row),
|
||||
},
|
||||
],
|
||||
}}
|
||||
renderExpanded={(row) => (
|
||||
<div className="p-3 bg-light border rounded">
|
||||
|
||||
@ -136,76 +136,76 @@ export default function PmsGrid({
|
||||
return (
|
||||
<div className="pms-grid">
|
||||
<div className="row mb-2">
|
||||
<div className="col-8">
|
||||
<div className="d-flex flex-row gap-2 gap-2 ">
|
||||
<div>
|
||||
<div className="col-8">
|
||||
<div className="d-flex flex-row gap-2 gap-2 ">
|
||||
<div>
|
||||
{features.search && (
|
||||
<input
|
||||
type="search"
|
||||
className="form-control form-control-sm"
|
||||
placeholder="Search..."
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{features.export && (
|
||||
<button
|
||||
className="btn btn-sm btn-outline-secondary"
|
||||
onClick={() =>
|
||||
exportToCSV(
|
||||
currentRows,
|
||||
colState.filter((c) => c.visible)
|
||||
)
|
||||
}
|
||||
>
|
||||
Export CSV
|
||||
</button>
|
||||
)}
|
||||
<input
|
||||
type="search"
|
||||
className="form-control form-control-sm"
|
||||
placeholder="Search..."
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{features.export && (
|
||||
<button
|
||||
className="btn btn-sm btn-outline-secondary"
|
||||
onClick={() =>
|
||||
exportToCSV(
|
||||
currentRows,
|
||||
colState.filter((c) => c.visible)
|
||||
)
|
||||
}
|
||||
>
|
||||
Export CSV
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-4 ">
|
||||
<div className="d-flex justify-content-end gap-2">
|
||||
{features.columnVisibility && (
|
||||
<ColumnVisibilityPanel
|
||||
columns={colState}
|
||||
onToggle={(k) =>
|
||||
updateColumn(k, {
|
||||
visible: !colState.find((c) => c.key === k).visible,
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{features.pageSizeSelector && (
|
||||
<select
|
||||
className="form-select form-select-sm"
|
||||
style={{ width: "80px" }}
|
||||
value={pageSize}
|
||||
onChange={(e) => setPageSize(Number(e.target.value))}
|
||||
>
|
||||
{[10, 25, 50, 100].map((n) => (
|
||||
<option key={n} value={n}>
|
||||
{n}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
<div className="col-4 ">
|
||||
<div className="d-flex justify-content-end gap-2">
|
||||
{features.columnVisibility && (
|
||||
<ColumnVisibilityPanel
|
||||
columns={colState}
|
||||
onToggle={(k) =>
|
||||
updateColumn(k, {
|
||||
visible: !colState.find((c) => c.key === k).visible,
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{features.pageSizeSelector && (
|
||||
<select
|
||||
className="form-select form-select-sm"
|
||||
style={{ width: "80px" }}
|
||||
value={pageSize}
|
||||
onChange={(e) => setPageSize(Number(e.target.value))}
|
||||
>
|
||||
{[10, 25, 50, 100].map((n) => (
|
||||
<option key={n} value={n}>
|
||||
{n}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
className="grid-wrapper text-nowrap"
|
||||
className="grid-wrapper text-nowrap" id="both-scrollbars-example"
|
||||
style={{ maxHeight: features.maxHeight || "60vh" }}
|
||||
>
|
||||
<table className="table table-sm mb-0">
|
||||
<table className="table table-sm roudned mb-0">
|
||||
<thead
|
||||
className="bg-light-secondary"
|
||||
style={{ position: "sticky", top: 0, zIndex: 3 }}
|
||||
className="bg-light-secondary border p-2 bg-light rounded"
|
||||
style={{ position: "sticky", top: 0, zIndex: 10 }}
|
||||
>
|
||||
<tr>
|
||||
<tr className="p-3">
|
||||
{features.selection && (
|
||||
<th style={{ width: 32 }} className="text-center">
|
||||
<th style={{ width: 32,position: "sticky", top: 0, zIndex: 10 }} className="text-center ticky-action-column">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input mx-3"
|
||||
@ -239,11 +239,11 @@ export default function PmsGrid({
|
||||
onDragOver={(e) => e.preventDefault()}
|
||||
onDrop={(e) => onDrop(e, col.key)}
|
||||
className={`pms-col-header vs-th ${
|
||||
col.pinned ? "pinned" : ""
|
||||
col.pinned ? "pinned z-6" : ""
|
||||
}`}
|
||||
style={style}
|
||||
>
|
||||
<div className="d-flex align-items-center justify-content-between px-1">
|
||||
<div className={`d-flex align-items-center justify-content-between px-1 ${col.pinned ? "z-6":""}`}>
|
||||
<div
|
||||
onClick={() => col.sortable && changeSort(col.key)}
|
||||
style={{ cursor: col.sortable ? "pointer" : "default" }}
|
||||
@ -271,7 +271,7 @@ export default function PmsGrid({
|
||||
)}
|
||||
{features.resizing && (
|
||||
<div
|
||||
className="resize-handle"
|
||||
className="resize-handle text-primary"
|
||||
onMouseDown={(e) => onResizeMouseDown(e, col.key)}
|
||||
/>
|
||||
)}
|
||||
@ -282,8 +282,8 @@ export default function PmsGrid({
|
||||
})}
|
||||
{features.actions && (
|
||||
<th
|
||||
className="text-center sticky-action-column vs-th bg-white"
|
||||
style={{ position: "sticky", right: 0, zIndex: 5 }}
|
||||
className="text-center sticky-action-column vs-th bg-white fw-semibold"
|
||||
style={{ position: "sticky", right: 0, zIndex: 10 }}
|
||||
>
|
||||
Actions
|
||||
</th>
|
||||
@ -367,42 +367,92 @@ export default function PmsGrid({
|
||||
|
||||
// render a single row (function hoisted so it can reference visibleColumns)
|
||||
function renderRow(row) {
|
||||
const isSelected = selected.has(row[rowKey]);
|
||||
|
||||
return (
|
||||
<React.Fragment key={row[rowKey]}>
|
||||
<tr className={`${selected.has(row[rowKey]) ? "bg-light" : ""}`}>
|
||||
<tr
|
||||
className={`align-middle ${
|
||||
isSelected ? "bg-light table-active" : ""
|
||||
}`}
|
||||
onClick={(e) => {
|
||||
// Optional: click row to select, but ignore if user clicked a button or checkbox
|
||||
if (e.target.tagName !== "BUTTON" && e.target.tagName !== "INPUT") {
|
||||
toggleSelect(row[rowKey]);
|
||||
}
|
||||
}}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
{features.selection && (
|
||||
<td className="text-center p-2">
|
||||
<td className="text-center p-2 .sticky-action-column" >
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
checked={selected.has(row[rowKey])}
|
||||
checked={isSelected}
|
||||
onChange={() => toggleSelect(row[rowKey])}
|
||||
/>
|
||||
</td>
|
||||
)}
|
||||
|
||||
{visibleColumns.map((col) => {
|
||||
const style = {
|
||||
minWidth: col.width || 120,
|
||||
width: col.width || undefined,
|
||||
};
|
||||
|
||||
if (col.pinned) style.position = "sticky";
|
||||
if (col.pinned === "left")
|
||||
style.left = `${getLeftOffset(colState, col.key)}px`;
|
||||
if (col.pinned === "right")
|
||||
style.right = `${getRightOffset(colState, col.key)}px`;
|
||||
|
||||
return (
|
||||
<td
|
||||
key={col.key}
|
||||
style={style}
|
||||
className={col.pinned ? "bg-white" : ""}
|
||||
className={`${col.className ?? ""} ${
|
||||
col.pinned ? "pinned-left px-3 bg-white pms-grid td pinned" : "px-3"
|
||||
}`}
|
||||
>
|
||||
{col.render ? col.render(row) : row[col.key] ?? ""}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
|
||||
{features.actions && (
|
||||
<td className="text-center sticky-action-column bg-white">
|
||||
{features.actions(row, toggleExpand)}
|
||||
<td
|
||||
className="text-center sticky-action-column bg-white"
|
||||
style={{
|
||||
position: "sticky",
|
||||
right: 0,
|
||||
zIndex: 2,
|
||||
width: "1%",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="d-inline-flex justify-content-center align-items-center gap-2"
|
||||
style={{ minWidth: "fit-content", padding: "0 4px" }}
|
||||
>
|
||||
{Array.isArray(features.actions)
|
||||
? features.actions.map((act, i) => (
|
||||
<button
|
||||
key={i}
|
||||
type="button"
|
||||
className="btn btn-link p-0 border-0"
|
||||
title={act.label}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
act.onClick && act.onClick(row);
|
||||
}}
|
||||
>
|
||||
<i className={`bx ${act.icon}`} />
|
||||
</button>
|
||||
))
|
||||
: typeof features.actions === "function"
|
||||
? features.actions(row, toggleExpand)
|
||||
: null}
|
||||
</div>
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
@ -477,4 +527,3 @@ function ColumnVisibilityPanel({ columns, onToggle }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
cursor: col-resize;
|
||||
height: 28px;
|
||||
display: inline-block;
|
||||
|
||||
}
|
||||
|
||||
.pms-col-header.pinned {
|
||||
@ -32,3 +33,22 @@
|
||||
z-index: 5;
|
||||
background: #fff;
|
||||
}
|
||||
.pms-grid td.pinned,
|
||||
.pms-grid th.pinned {
|
||||
position: sticky;
|
||||
background: #fff;
|
||||
z-index: 2;
|
||||
box-shadow: 2px 0 4px rgba(0, 0, 0, 0.05);
|
||||
border-right: 1px solid #dee2e6 !important;
|
||||
}
|
||||
.pms-grid td.pinned-left,
|
||||
.pms-grid th.pinned-left {
|
||||
z-index: 3;
|
||||
box-shadow: 2px 0 3px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.pms-grid td.pinned-right,
|
||||
.pms-grid th.pinned-right {
|
||||
z-index: 3;
|
||||
box-shadow: -2px 0 3px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user