diff --git a/public/assets/vendor/css/core.css b/public/assets/vendor/css/core.css index 244677ff..fcb1fcc4 100644 --- a/public/assets/vendor/css/core.css +++ b/public/assets/vendor/css/core.css @@ -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 { diff --git a/src/assets/vendor/css/core.css b/src/assets/vendor/css/core.css index 70f4fb09..52d022e3 100644 --- a/src/assets/vendor/css/core.css +++ b/src/assets/vendor/css/core.css @@ -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; diff --git a/src/services/pmsGrid/BasicTable.jsx b/src/services/pmsGrid/BasicTable.jsx index 885a2396..b1bd3399 100644 --- a/src/services/pmsGrid/BasicTable.jsx +++ b/src/services/pmsGrid/BasicTable.jsx @@ -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) => ₹{r.rate.toLocaleString()}, }, { @@ -151,27 +655,38 @@ const boqColumns = [ title: "Amount (₹)", sortable: true, width: 130, + className: "text-end", render: (r) => ( - + ₹{(r.quantity * r.rate).toLocaleString()} ), 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) => ( { + 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 (
- -
({ @@ -225,19 +738,24 @@ useEffect(() => { // groupByKey: "category", aggregation: true, expand: true, - maxHeight: "60vh", - actions: (row, toggleExpand) => ( - <> - - - ), + 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) => (
diff --git a/src/services/pmsGrid/PmsGrid.jsx b/src/services/pmsGrid/PmsGrid.jsx index 435bbe7f..ce9e474b 100644 --- a/src/services/pmsGrid/PmsGrid.jsx +++ b/src/services/pmsGrid/PmsGrid.jsx @@ -136,76 +136,76 @@ export default function PmsGrid({ return (
-
-
-
+
+
+
{features.search && ( - setSearch(e.target.value)} - /> - )} -
- {features.export && ( - - )} + setSearch(e.target.value)} + /> + )} +
+ {features.export && ( + + )} +
-
-
-
- {features.columnVisibility && ( - - updateColumn(k, { - visible: !colState.find((c) => c.key === k).visible, - }) - } - /> - )} - {features.pageSizeSelector && ( - - )} +
+
+ {features.columnVisibility && ( + + updateColumn(k, { + visible: !colState.find((c) => c.key === k).visible, + }) + } + /> + )} + {features.pageSizeSelector && ( + + )} +
-
- +
- + {features.selection && ( - @@ -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 ( - + { + // 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 && ( - )} + {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 ( ); })} + {features.actions && ( - )} @@ -477,4 +527,3 @@ function ColumnVisibilityPanel({ columns, onToggle }) { ); } - \ No newline at end of file diff --git a/src/services/pmsGrid/pms-grid.css b/src/services/pmsGrid/pms-grid.css index e8f28dd6..dba7a0c0 100644 --- a/src/services/pmsGrid/pms-grid.css +++ b/src/services/pmsGrid/pms-grid.css @@ -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); +}
+ e.preventDefault()} onDrop={(e) => onDrop(e, col.key)} className={`pms-col-header vs-th ${ - col.pinned ? "pinned" : "" + col.pinned ? "pinned z-6" : "" }`} style={style} > -
+
col.sortable && changeSort(col.key)} style={{ cursor: col.sortable ? "pointer" : "default" }} @@ -271,7 +271,7 @@ export default function PmsGrid({ )} {features.resizing && (
onResizeMouseDown(e, col.key)} /> )} @@ -282,8 +282,8 @@ export default function PmsGrid({ })} {features.actions && (
Actions
+ toggleSelect(row[rowKey])} /> {col.render ? col.render(row) : row[col.key] ?? ""} - {features.actions(row, toggleExpand)} + +
+ {Array.isArray(features.actions) + ? features.actions.map((act, i) => ( + + )) + : typeof features.actions === "function" + ? features.actions(row, toggleExpand) + : null} +