diff --git a/src/bonsai/bonsai/bim/data/webui/sioserver.py b/src/bonsai/bonsai/bim/data/webui/sioserver.py index fe49b03bdd..9d1651b0a9 100644 --- a/src/bonsai/bonsai/bim/data/webui/sioserver.py +++ b/src/bonsai/bonsai/bim/data/webui/sioserver.py @@ -166,6 +166,16 @@ class BlenderNamespace(socketio.AsyncNamespace): blender_messages[sid]["cost_schedules"] = data await sio.emit("cost_schedules", {"blenderId": sid, "data": data}, namespace="/web") + async def on_cost_values(self, sid, data): + print(f"Cost values from Blender client {sid}") + blender_messages[sid]["cost_values"] = data + await sio.emit("cost_values", {"blenderId": sid, "data": data}, namespace="/web") + + async def on_cost_value(self, sid, data): + print(f"Cost values from Blender client {sid}") + blender_messages[sid]["cost_value"] = data + await sio.emit("cost_value", {"blenderId": sid, "data": data}, namespace="/web") + async def schedules(request): with open("templates/index.html", "r") as f: template = f.read() diff --git a/src/bonsai/bonsai/bim/data/webui/static/css/components/contextMenu.css b/src/bonsai/bonsai/bim/data/webui/static/css/components/contextMenu.css new file mode 100644 index 0000000000..79ba11cc68 --- /dev/null +++ b/src/bonsai/bonsai/bim/data/webui/static/css/components/contextMenu.css @@ -0,0 +1,89 @@ + +th { + position: relative; +} +.resizer { + position: absolute; + right: 0; + top: 0; + width: 5px; + height: 100%; + cursor: col-resize; + user-select: none; +} +.context-menu { + display: none; + position: absolute; + background-color: rgb(51, 51, 51); + border: 1px solid #ccc; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); + z-index: 1000; + color: white; +} +.context-menu button { + display: block; + width: 100%; + padding: 8px; + border: none; + background: none; + text-align: left; + cursor: pointer; +} +.context-menu button:hover { + background-color: #f0f0f0; + color: black; +} + +/* Context menu button is clicked, should zoom into object and change color */ +.context-menu button:active { + background-color: #ff9634; + color: rgba(37, 51, 77, 0.466); + /* zoom */ + transform: scale(0.9); +} + +#cost-items tr:hover { + background-color: #f0f0f0; + color : black; +} + +.form-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; + background-color: #363636; + padding: 10px; +} + +.cost-values-form { + position: absolute; /* Set initial position to absolute */ + background-color: white; + border: 1px solid black; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + z-index: 9999; + cursor: move; /* Change cursor to move */ +} + +.cost-values-table { + width: 100%; + border-collapse: collapse; +} + +.cost-values-table th, .cost-values-table td { + border: 1px solid #ddd; + padding: 8px; +} + +.cost-values-table th { + background-color: #f2f2f2; + text-align: left; +} + +.close-button { + position: absolute; + top: 10px; + right: 10px; + font-size: 20px; + cursor: pointer; +} \ No newline at end of file diff --git a/src/bonsai/bonsai/bim/data/webui/static/css/gantt.css b/src/bonsai/bonsai/bim/data/webui/static/css/gantt.css index 035fd68f0b..36575b8c74 100644 --- a/src/bonsai/bonsai/bim/data/webui/static/css/gantt.css +++ b/src/bonsai/bonsai/bim/data/webui/static/css/gantt.css @@ -1,4 +1,5 @@ @import url("./components/card.css"); +@import url("./components/contextMenu.css"); :root { --font-family: Arial, sans-serif; diff --git a/src/bonsai/bonsai/bim/data/webui/static/js/cost.js b/src/bonsai/bonsai/bim/data/webui/static/js/cost.js index 1ea91590ed..5a10017b89 100644 --- a/src/bonsai/bonsai/bim/data/webui/static/js/cost.js +++ b/src/bonsai/bonsai/bim/data/webui/static/js/cost.js @@ -23,6 +23,8 @@ function connectSocket() { socket.on("connect", handleWebConnect); socket.on("cost_schedules", handleCostSchedulesData); socket.on("cost_items", handleCostItemsData); + socket.on("cost_values", handleCostValuesData); + socket.on("cost_value", handleCostValueData); } function handleBlenderConnect(blenderId) { @@ -46,8 +48,20 @@ function handleBlenderDisconnect(blenderId) { }); } +function removeTableElement(blenderId) { + $("#cost-items-" + blenderId).remove(); +} +function handleCostValueData(data) { + + const costItemId = data.data["cost_value"]["cost_item_id"]; + const costValueId = data.data["cost_value"]["cost_value_id"]; + console.log("Handling cost value data", costItemId, costValueId); + CostUI.addNewCostValueRow(costItemId, costValueId); + +} + function handleConnectedClients(data) { $("#blender-count").text(data.length); @@ -56,6 +70,29 @@ function handleConnectedClients(data) { }); } +function handleCostValuesData(data) { + CostUI.createCostValuesForm({ + costValues: data.data["cost_values"]["cost_values"], + costItemId: data.data["cost_values"]["cost_item_id"], + callbacks: { + 'editCostValues': editCostValues, + 'addCostValue': addCostValue, + }, + }); +} + +function addCostValue(costItemId) { + executeOperator({ type: "addCostValue", costItemId: costItemId }); +} + +function editCostValues(costItemId, costValues) { + executeOperator({ + type: "editCostValues", + costItemId: costItemId, + costValues: costValues + }); +} + function handleThemeData(themeData) { function arrayToRgbString(arr) { const [r, g, b, a] = arr.map((num) => Math.round(num * 255)); @@ -99,7 +136,6 @@ function setTheme(theme) { } function addCostItem(costItemId) { - console.log("addCostItem", costItemId); executeOperator({ type: "addCostItem", costItemId: costItemId }); } @@ -119,11 +155,7 @@ function handleCostSchedulesData(data) { const blenderId = data.blenderId; const costSchedules = data.data["cost_schedules"]["cost_schedules"]; const currency = data.data["cost_schedules"]["currency"]["name"]; - - console.log(data.data["cost_schedules"]); - const costScheduleDiv = $("#cost-schedules"); - costSchedules.forEach((costSchedule) => { costSchedule.UpdateDate = new Date(costSchedule.UpdateDate); const mainContainer = CostUI.text("Updated On: " + costSchedule.UpdateDate); @@ -136,7 +168,6 @@ function handleCostSchedulesData(data) { } function handleCostItemsData(data) { - console.log(data); CostUI.createCostSchedule({ data: data.data["cost_items"], blenderID: data.blenderId, @@ -144,6 +175,7 @@ function handleCostItemsData(data) { "addCostItem": addCostItem, "selectAssignedElements": selectAssignedElements, 'editCostItemName': editCostItemName, + 'enableEditingCostValues': enableEditingCostValues, } }); } @@ -166,3 +198,7 @@ function loadCostSchedule(costScheduleId, blenderId) { function getCostSchedules(blenderId) { executeOperator({ type: "getCostSchedules" }, blenderId); } + +function enableEditingCostValues(costItemId) { + executeOperator({ type: "enableEditingCostValues", costItemId: costItemId}); +} \ No newline at end of file diff --git a/src/bonsai/bonsai/bim/data/webui/static/js/gantt.js b/src/bonsai/bonsai/bim/data/webui/static/js/gantt.js index 5942765dfd..473cab82d9 100644 --- a/src/bonsai/bonsai/bim/data/webui/static/js/gantt.js +++ b/src/bonsai/bonsai/bim/data/webui/static/js/gantt.js @@ -82,7 +82,6 @@ function handleBlenderDisconnect(blenderId) { function handleConnectedClients(data) { $("#blender-count").text(data.length); - // console.log(data); data.forEach(function (id) { connectedClients[id] = { shown: false, @@ -115,7 +114,6 @@ function handleThemeData(themeData) { } const cssRule = generateCssVariableRule(themeData.theme); - console.log(cssRule); var styleElement = $("#gantt-stylesheet")[0]; if (styleElement) { @@ -133,7 +131,6 @@ function handleWorkScheduleData(data) { const workSchedules = data["data"]["work_schedule_info"]; workSchedules.forEach((workSchedule) => { - console.log(workSchedule); const mainContainer = CostUI.text(new Date(workSchedule.CreationDate).toLocaleDateString()); const callback = () => loadWorkSchedule(workSchedule.id); const card = CostUI.createCard(workSchedule.Name,mainContainer, callback); @@ -142,11 +139,8 @@ function handleWorkScheduleData(data) { } function handleGanttData(data) { - console.log("running handleGanttData"); const blenderId = data["blenderId"]; - console.log(data); - const filename = data["data"]["ifc_file"]; const ganttTasks = data["data"]["gantt_data"]["tasks"]; const ganttWorkSched = data["data"]["gantt_data"]["work_schedule"]; @@ -183,7 +177,6 @@ function handleDefaultData(data) { const blenderId = data["blenderId"]; const isDirty = data["data"]["is_dirty"]; showWarning(blenderId, isDirty); - console.log('default data',data); } // Function to add a new gantt with data and filename diff --git a/src/bonsai/bonsai/bim/data/webui/static/js/utilities/costui.js b/src/bonsai/bonsai/bim/data/webui/static/js/utilities/costui.js index 61779592bd..7ec2d6f582 100644 --- a/src/bonsai/bonsai/bim/data/webui/static/js/utilities/costui.js +++ b/src/bonsai/bonsai/bim/data/webui/static/js/utilities/costui.js @@ -1,14 +1,6 @@ export class CostUI { constructor() {} - createButton() { - console.log("Button created"); - } - - createInput() { - console.log("Input created"); - } - static isCostScheduleLoaded(id) { const existingTable = document.getElementById('cost-items-' + id); return existingTable !== null; @@ -17,7 +9,7 @@ export class CostUI { static removeCostSchedule(id) { document.getElementById("cost-items-" + id).remove(); } - static createTable(id) { + static createTable(id, callbacks) { CostUI.isCostScheduleLoaded(id) ? CostUI.removeCostSchedule(id) : null; const table = document.createElement("table"); @@ -51,7 +43,7 @@ export class CostUI { CostUI.addTableStyles(id); // Create context menu - CostUI.createContextMenu(); + CostUI.createContextMenu(callbacks); table.get_blender_id = function() { return this.getAttribute("id").split("-")[2]; @@ -71,52 +63,19 @@ export class CostUI { #cost-items-${id} td:not(:nth-child(1)) { width: 100px; /* Set a fixed width for other columns */ } - th { - position: relative; - } - .resizer { - position: absolute; - right: 0; - top: 0; - width: 5px; - height: 100%; - cursor: col-resize; - user-select: none; - } - .context-menu { - display: none; - position: absolute; - background-color: white; - border: 1px solid #ccc; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); - z-index: 1000; - } - .context-menu button { - display: block; - width: 100%; - padding: 8px; - border: none; - background: none; - text-align: left; - cursor: pointer; - } - .context-menu button:hover { - background-color: #f0f0f0; - } - #cost-items tr:hover { - background-color: #f0f0f0; /* Change this color to your desired hover color */ - } + `; document.head.appendChild(style); } - static createContextMenu() { + static createContextMenu(callbacks) { // Create context menu const contextMenu = document.createElement("div"); contextMenu.id = "context-menu"; contextMenu.classList.add("context-menu"); contextMenu.innerHTML = ` - + + `; @@ -126,6 +85,7 @@ export class CostUI { document.addEventListener("contextmenu", function(event) { event.preventDefault(); const targetRow = event.target.closest("tr"); + const targetCell = event.target.closest("td"); if (targetRow && targetRow.parentElement.id === "cost-items") { const contextMenu = document.getElementById("context-menu"); contextMenu.style.display = "block"; @@ -134,6 +94,7 @@ export class CostUI { // Store the target row in the context menu for later use contextMenu.targetRow = targetRow; + contextMenu.targetCell = targetCell; } else { document.getElementById("context-menu").style.display = "none"; } @@ -146,14 +107,32 @@ export class CostUI { } }); - document.getElementById("edit-button").addEventListener("click", function() { + const editCostValuesButton = document.getElementById("edit-cost-values-button"); + editCostValuesButton.addEventListener("click", function() { + console.log("Edit cost values executed"); const targetRow = document.getElementById("context-menu").targetRow; + const targetCell = document.getElementById("context-menu").targetCell; if (targetRow) { - // Implement your edit action here - console.log("Edit row:", targetRow.getAttribute("id")); + const costItemId = parseInt(targetRow.getAttribute("id")); + callbacks.enableEditingCostValues ? callbacks.enableEditingCostValues(costItemId) : null; } }); + const addButton = document.getElementById("add-cost-item-button"); + if (!addButton.dataset.listenerAdded) { + function addCostItemHandler(e) { + e.stopPropagation(); + const targetRow = document.getElementById("context-menu").targetRow; + if (targetRow) { + const costItemId = parseInt(targetRow.getAttribute("id")); + callbacks.addCostItem ? callbacks.addCostItem(costItemId) : null; + } + document.getElementById("context-menu").style.display = "none"; + } + addButton.addEventListener("click", addCostItemHandler); + addButton.dataset.listenerAdded = "true"; + } + document.getElementById("delete-button").addEventListener("click", function() { const targetRow = document.getElementById("context-menu").targetRow; if (targetRow) { @@ -246,7 +225,7 @@ export class CostUI { } static createCostSchedule({ data, blenderID, title, callbacks = {} }) { - const [table, tbody] = CostUI.createTable(blenderID); + const [table, tbody] = CostUI.createTable(blenderID, callbacks); CostUI.createCostItem(data, tbody, 0, null, callbacks); CostUI.applyExpandedState(); } @@ -263,6 +242,35 @@ export class CostUI { } static createRow(obj, nestingLevel, parentID, callbacks = {}) { + const row = CostUI.createTableRow(obj, nestingLevel, parentID); + const expandButton = CostUI.createExpandButton(obj); + const nameCell = CostUI.createNameCell(obj, nestingLevel, expandButton, callbacks); + const totalCostQuantityCell = CostUI.createTableCell(obj.TotalCostQuantity); + const unitSymbolCell = CostUI.createTableCell(obj.UnitSymbol); + const totalAppliedValueCell = CostUI.createTableCell(obj.TotalAppliedValue); + const totalCostCell = CostUI.createTotalCostCell(obj); + const flexContainerCell = CostUI.createFlexContainerCell(obj, callbacks); + + row.appendChild(nameCell); + row.appendChild(totalCostQuantityCell); + row.appendChild(unitSymbolCell); + row.appendChild(totalAppliedValueCell); + row.appendChild(totalCostCell); + row.appendChild(flexContainerCell); + + row.get_id = function() { + return this.getAttribute("id"); + }; + + row.get_parent = function() { + const parentId = this.getAttribute("parent-id"); + return parentId ? document.getElementById(parentId) : null; + }; + + return row; + } + + static createTableRow(obj, nestingLevel, parentID) { const row = document.createElement("tr"); row.setAttribute("id", obj.id); row.setAttribute("parent-id", parentID); @@ -270,6 +278,10 @@ export class CostUI { row.classList.add("nested"); row.classList.add(`level-${nestingLevel}`); } + return row; + } + + static createExpandButton(obj) { const expandButton = document.createElement("button"); expandButton.classList.add("toggle-button"); if (obj.is_nested_by && obj.is_nested_by.length > 0) { @@ -277,51 +289,62 @@ export class CostUI { } else { expandButton.style.visibility = "hidden"; } - //row.appendChild(expandButton); - expandButton.addEventListener("click", function() { CostUI.contractExpandRow.call(this, obj.id); }); - + return expandButton; + } + + static createNameCell(obj, nestingLevel, expandButton, callbacks) { const nameCell = document.createElement("td"); const nameInput = document.createElement("input"); nameInput.value = obj.name ? obj.name : "Unnamed"; - + nameInput.addEventListener("change", function() { callbacks.editCostItemName ? callbacks.editCostItemName(obj.id, this.value) : null; }); - + nameInput.addEventListener("keydown", function(event) { if (event.key === "Enter") { callbacks.editCostItemName ? callbacks.editCostItemName(obj.id, this.value) : null; } }); + nameCell.style.paddingLeft = nestingLevel * 20 + "px"; nameCell.appendChild(expandButton); nameCell.appendChild(nameInput); - row.appendChild(nameCell); - - const totalCostQuantityCell = document.createElement("td"); - totalCostQuantityCell.textContent = obj.TotalCostQuantity; - row.appendChild(totalCostQuantityCell); - - const unitSymbolCell = document.createElement("td"); - unitSymbolCell.textContent = obj.UnitSymbol; - row.appendChild(unitSymbolCell); - - const totalAppliedValueCell = document.createElement("td"); - totalAppliedValueCell.textContent = obj.TotalAppliedValue; - row.appendChild(totalAppliedValueCell); - + return nameCell; + } + + static createTableCell(content) { + const cell = document.createElement("td"); + cell.textContent = content; + return cell; + } + + static createTotalCostCell(obj) { const totalCostCell = document.createElement("td"); const totalCost = parseFloat(obj.TotalCost).toFixed(2); - totalCostCell.textContent = obj.is_sum ? totalCost + " (Σ)" : totalCost; - - row.appendChild(totalCostCell); - + return totalCostCell; + } + + static createFlexContainerCell(obj, callbacks) { const divFlex = document.createElement("div"); divFlex.classList.add("flex-container"); + + const addButton = CostUI.createAddButton(obj, callbacks); + const selectButton = CostUI.createSelectButton(obj, callbacks); + + divFlex.appendChild(addButton); + divFlex.appendChild(selectButton); + + const flexContainerCell = document.createElement("td"); + flexContainerCell.appendChild(divFlex); + return flexContainerCell; + } + + static createAddButton(obj, callbacks) { const addButton = document.createElement("button"); addButton.textContent = "+"; addButton.classList.add("add-button"); @@ -329,31 +352,17 @@ export class CostUI { e.stopPropagation(); callbacks.addCostItem ? callbacks.addCostItem(obj.id) : null; }); - + return addButton; + } + + static createSelectButton(obj, callbacks) { const selectButton = document.createElement("button"); selectButton.textContent = "Select"; selectButton.addEventListener("click", function(e) { e.stopPropagation(); callbacks.selectAssignedElements ? callbacks.selectAssignedElements(obj.id) : null; }); - - divFlex.appendChild(addButton); - divFlex.appendChild(selectButton); - - const flexContainerCell = document.createElement("td"); - flexContainerCell.appendChild(divFlex); - row.appendChild(flexContainerCell); - - row.get_id = function() { - return this.getAttribute("id"); - }; - - row.get_parent = function() { - const parentId = this.getAttribute("parent-id"); - return parentId ? document.getElementById(parentId) : null; - }; - - return row; + return selectButton; } static hideNestedRows(parentId) { @@ -461,4 +470,267 @@ export class CostUI { return card; } + + static createCostValuesForm({ costItemId, costValues, callbacks }) { + // Check if the form already exists and remove it if it does + const formId = "cost-values-form-" + costItemId; + let existingForm = document.getElementById(formId); + if (existingForm) { + existingForm.remove(); + } + + const form = CostUI.createFormElement(formId, "cost-values-form"); + + const header = CostUI.createHeader("Cost Values Form"); + form.appendChild(header); + + const table = CostUI.createCostValuesTable(costItemId, ["Type", "Category", "Value"]); + + costValues.forEach(costValue => { + const tr = CostUI.createCostvaluesRow(costItemId, costValue); + table.appendChild(tr); + }); + + form.appendChild(table); + + const addButton = CostUI.createAddCostValueButton(costItemId, callbacks); + form.appendChild(addButton); + + const submitButton = CostUI.createSubmitButton(callbacks); + form.appendChild(submitButton); + + const closeButton = CostUI.createCloseButton(form); + form.appendChild(closeButton); + + CostUI.makeHeaderDraggable(header, form); + + document.body.appendChild(form); + + return form; + } + + + static getCostValuesTable(costItemId) { + return document.getElementById("cost-values-table-" + costItemId); + } + + static addNewCostValueRow(costItemId, costValueId) { + const table = CostUI.getCostValuesTable(costItemId); + // check if table exists + if (!table) { + console.log("Cost values table not found for cost item ID:", costItemId); + return; + } + console.log("Adding new cost value row", costValueId); + console.log(table) + const tr = CostUI.createCostvaluesRow(costItemId, {"category": "", "name": "", "applied_value": 0, "id": costValueId, "parent": costItemId}); + table.appendChild(tr); + } + + static createCostValuesTable(costItemId, headers) { + const table = document.createElement("table"); + table.classList.add("cost-values-table"); + table.id = "cost-values-table-" + costItemId; + + const headerRow = document.createElement("tr"); + headers.forEach(headerText => { + const th = document.createElement("th"); + th.textContent = headerText; + headerRow.appendChild(th); + }); + table.appendChild(headerRow); + + return table; + } + + static createCostvaluesRow(costItemId,costValue) { + + function cleanLabel(value) { + // remove anything which is not a dot or a digit + return value.replace(/[^0-9.]/g, ''); + + } + + + if (document.getElementById(costValue.id)) { + return document.getElementById(costValue.id); + } + + const tr = document.createElement("tr"); + tr.isCostValue = true; + tr.parent = costItemId; + tr.id = costValue.id; + + let costType = "FIXED"; + if (costValue.category === "*") { + costType = "SUM"; + } + else if (costValue.category && costValue.category !== "*") { + costType = "CATEGORY"; + } + else if (costValue.applied_value) { + costType = "FIXED"; + } + + const typeCell = CostUI.createTableDropdown("type", costType); + const dropdown = typeCell.querySelector("select"); + dropdown.addEventListener("change", function() { + const selectedType = this.value; + CostUI.updateRowBasedOnType(selectedType, categoryCell, valueCell1); + }); + tr.appendChild(typeCell); + + const categoryCell = CostUI.createTableInput("text", "category", costValue.category); + tr.appendChild(categoryCell); + + let value + + if (costValue.category === "*"){ + value = cleanLabel(costValue.label) + } + else { + value = costValue.applied_value + } + const valueCell1 = CostUI.createTableInput("number", "value", value); + tr.appendChild(valueCell1); + // Apply initial state based on costType + CostUI.updateRowBasedOnType(costType, categoryCell, valueCell1); + + return tr; + } + + static updateRowBasedOnType(type, categoryCell, valueCell1) { + if (type === "FIXED") { + categoryCell.querySelector("input").disabled = true; + categoryCell.querySelector("input").style.backgroundColor = "lightgrey"; + valueCell1.querySelector("input").disabled = false; + valueCell1.querySelector("input").style.backgroundColor = ""; + } else if (type === "CATEGORY") { + categoryCell.querySelector("input").disabled = false; + categoryCell.querySelector("input").style.backgroundColor = ""; + valueCell1.querySelector("input").disabled = false; + valueCell1.querySelector("input").style.backgroundColor = ""; + } else if (type === "SUM") { + categoryCell.querySelector("input").disabled = true; + categoryCell.querySelector("input").style.backgroundColor = "lightgrey"; + valueCell1.querySelector("input").disabled = true; + valueCell1.querySelector("input").style.backgroundColor = "lightgrey"; + } + } + + static createTableDropdown(name, value="FIXED") { + const cell = document.createElement("td"); + const dropdown = document.createElement("select"); + dropdown.name = name; + + const options = ["FIXED", "CATEGORY", "SUM"]; + options.forEach(optionValue => { + const option = document.createElement("option"); + option.value = optionValue; + option.textContent = optionValue; + if (optionValue === value) { + option.selected = true; + } + dropdown.appendChild(option); + }); + + cell.appendChild(dropdown); + return cell; + } + + static createTableInput(type, name, value) { + const cell = document.createElement("td"); + const input = document.createElement("input"); + input.type = type; + input.name = name; + input.value = value; + cell.appendChild(input); + return cell; + } + + static createAddCostValueButton(costItemId, callbacks) { + const addButton = document.createElement("button"); + addButton.textContent = "+"; + addButton.classList.add("add-button"); + addButton.addEventListener("click", function(e) { + e.preventDefault(); + callbacks.addCostValue ? callbacks.addCostValue(costItemId) : null; + }); + return addButton; + } + + static createSubmitButton(callbacks) { + const submitButton = document.createElement("button"); + submitButton.type = "submit"; + submitButton.textContent = "Save"; + + submitButton.addEventListener("click", function(e) { + e.preventDefault(); + const form = this.closest("form"); + const costValues = []; + const rows = Array.from(form.querySelectorAll("tr")).filter(row => row.isCostValue); + + rows.forEach(row => { + const costCategory = row.querySelector("input[name='category']").value; + const appliedValue = parseFloat(row.querySelector("input[name='value']").value); + const costType = row.querySelector("select[name='type']").value; + const id = parseInt(row.id); + costValues.push({ costType, costCategory, appliedValue, id: id, costItemId: row.parent }); + }); + const costItemId = parseInt(form.id.split("-")[3]); + console.log(form.id) + console.log("Cost values to be saved:", costItemId); + callbacks.editCostValues ? callbacks.editCostValues(costItemId, costValues) : null; + form.remove(); + }); + return submitButton; + } + + static createFormElement(id, className) { + const form = document.createElement("form"); + form.id = id; + form.classList.add(className); + form.style.position = "absolute"; // Set initial position to absolute + form.style.top = "50%"; + form.style.left = "50%"; + form.style.transform = "translate(-50%, -50%)"; + return form; + } + + static createHeader(text) { + const header = document.createElement("div"); + header.classList.add("form-header"); + header.textContent = text; + return header; + } + + static createCloseButton(form) { + const closeButton = document.createElement("span"); + closeButton.classList.add("close-button"); + closeButton.innerHTML = "×"; + closeButton.addEventListener("click", function() { + form.remove(); + }); + return closeButton; + } + + static makeHeaderDraggable(header, form) { + header.addEventListener("mousedown", function(e) { + let offsetX = e.clientX - form.getBoundingClientRect().left; + let offsetY = e.clientY - form.getBoundingClientRect().top; + + function mouseMoveHandler(e) { + form.style.left = `${e.clientX - offsetX}px`; + form.style.top = `${e.clientY - offsetY}px`; + } + + function mouseUpHandler() { + document.removeEventListener("mousemove", mouseMoveHandler); + document.removeEventListener("mouseup", mouseUpHandler); + } + + document.addEventListener("mousemove", mouseMoveHandler); + document.addEventListener("mouseup", mouseUpHandler); + }); + } } diff --git a/src/bonsai/bonsai/bim/data/webui/templates/costing.html b/src/bonsai/bonsai/bim/data/webui/templates/costing.html index 6bd6ddeee7..c0297bfac4 100644 --- a/src/bonsai/bonsai/bim/data/webui/templates/costing.html +++ b/src/bonsai/bonsai/bim/data/webui/templates/costing.html @@ -4,8 +4,7 @@