Cost module web UI:

- quick button to add a SUM cost value
- style improvements
This commit is contained in:
myoualid
2024-09-18 20:34:31 +01:00
parent 116a0f1a02
commit 45708ee287
4 changed files with 110 additions and 68 deletions
@@ -8,6 +8,7 @@
--background-color: #363636; --background-color: #363636;
--button-background: #28a745; --button-background: #28a745;
--button-hover-background: #218838; --button-hover-background: #218838;
--highlight-over: #ED5700;
--scrollbar-track-color: #f1f1f1; --scrollbar-track-color: #f1f1f1;
--scrollbar-thumb-color: #888; --scrollbar-thumb-color: #888;
--scrollbar-thumb-hover-color: #555; --scrollbar-thumb-hover-color: #555;
@@ -95,8 +96,7 @@ body {
display: inline-block; display: inline-block;
transition: background-color 0.3s ease; transition: background-color 0.3s ease;
width: fit-content; width: fit-content;
margin: 0.5em; margin: 0.25em;
z-index: 11000;
} }
.action-button::after { .action-button::after {
@@ -116,11 +116,12 @@ body {
.action-button:hover::after { .action-button:hover::after {
opacity: 1; opacity: 1;
border-color: var(--highlight-over);
border-width: 2px;
} }
.active-btn { .active-btn {
background-color: #887821; background-color: #887821;
/* make a nic shadow */
box-shadow: 0 0 10px #887821; box-shadow: 0 0 10px #887821;
} }
@@ -246,7 +247,7 @@ td {
align-items: center; align-items: center;
} }
td:first-child { #cost-items td:first-child {
display: flex; display: flex;
align-items: center; align-items: center;
} }
@@ -281,29 +282,26 @@ input:focus {
color: #ddd; color: #ddd;
} }
#cost-items input, #cost-items #cost-items input{
select {
border-radius: 6px; border-radius: 6px;
padding: 6px 8px; padding: 6px 8px;
font-size: 14px; font-size: 14px;
line-height: 20px; line-height: 20px;
margin-left: 0.5em;
margin-right: 0.5em;
font-size: var(--fontSize); font-size: var(--fontSize);
min-width: 50px; min-width: 50px;
} }
form input { form input, form select {
border-radius: 6px; border-radius: 6px;
padding: 3px 4px; padding: 3px 4px;
font-size: 12px; font-size: 12px;
line-height: 10px; line-height: 10px;
margin-left: 0.2em;
margin-right: 0.2em;
font-size: inherit; font-size: inherit;
height: inherit; width: 100%;
width: inherit; height: 100%;
box-sizing: border-box; max-height: 30px;
max-width: 250px;
box-sizing: border-box;
} }
#cost-items tr:hover, #cost-items tr:hover,
@@ -399,7 +397,17 @@ form table {
} }
.clickable-cell:hover { .clickable-cell:hover {
border: 1px solid #94a728; border: 2px solid var(--highlight-over);
border-radius: 6px;
cursor: pointer; cursor: pointer;
} }
td {
position: relative;
}
.append-right {
right: 0;
top: 50%;
position: absolute;
transform: translateY(-50%);
}
@@ -54,14 +54,16 @@ function handleEditQuantities(data) {
}); });
} }
function addSumCostValue(costItemId) {
executeOperator({ type: "addSumCostValue", costItemId: costItemId });
}
function addQuantity(costItemId, ifcClass) { function addQuantity(costItemId, ifcClass) {
executeOperator({ executeOperator({
type: "AddCostItemQuantity", type: "AddCostItemQuantity",
costItemId: costItemId, costItemId: costItemId,
ifcClass: ifcClass, ifcClass: ifcClass,
}); });
//CostUI.addQuantity(costItemId, quantityName);
} }
function editQuantity(costItemId, quantityId, attributes) { function editQuantity(costItemId, quantityId, attributes) {
@@ -387,6 +389,7 @@ function handleCostItemsData(data) {
enableEditingCostValues: enableEditingCostValues, enableEditingCostValues: enableEditingCostValues,
addSummaryCostItem: addSummaryCostItem, addSummaryCostItem: addSummaryCostItem,
enableEditingQuantities: enableEditingQuantities, enableEditingQuantities: enableEditingQuantities,
addSumCostValue: addSumCostValue,
}, },
}); });
} }
@@ -32,7 +32,6 @@ export class CostUI {
for (let i = 0; i < columnHeaders.length; i++) { for (let i = 0; i < columnHeaders.length; i++) {
const th = document.createElement("th"); const th = document.createElement("th");
th.textContent = columnHeaders[i]; th.textContent = columnHeaders[i];
th.style.position = "relative";
tr.appendChild(th); tr.appendChild(th);
if (i < columnHeaders.length - 1) { if (i < columnHeaders.length - 1) {
@@ -417,12 +416,22 @@ export class CostUI {
}); });
} }
static format_number(number) {
return new Intl.NumberFormat("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
useGrouping: true,
})
.format(number)
.replace(/,/g, " ");
}
static addCostItemRow(costItem, nestingLevel, parentID, callbacks = {}) { static addCostItemRow(costItem, nestingLevel, parentID, callbacks = {}) {
const totalQuantity = costItem.TotalCostQuantity const totalQuantity = costItem.TotalCostQuantity
? parseFloat(costItem.TotalCostQuantity).toFixed(2) ? CostUI.format_number(costItem.TotalCostQuantity)
: "-"; : "-";
const appliedValue = costItem.TotalAppliedValue const appliedValue = costItem.TotalAppliedValue
? parseFloat(costItem.TotalAppliedValue).toFixed(2) ? CostUI.format_number(costItem.TotalAppliedValue)
: "-"; : "-";
const row = CostUI.createCostItemRow(costItem, nestingLevel, parentID); const row = CostUI.createCostItemRow(costItem, nestingLevel, parentID);
const expandButton = CostUI.createExpandButton(costItem); const expandButton = CostUI.createExpandButton(costItem);
@@ -435,9 +444,14 @@ export class CostUI {
const totalCostQuantityCell = CostUI.createTableCell(totalQuantity); const totalCostQuantityCell = CostUI.createTableCell(totalQuantity);
totalCostQuantityCell.classList.add("clickable-cell"); totalCostQuantityCell.classList.add("clickable-cell");
const unitSymbolCell = CostUI.createTableCell(costItem.UnitSymbol); const unitSymbolCell = CostUI.createTableCell(costItem.UnitSymbol);
const totalAppliedValueCell = CostUI.createTableCell(appliedValue);
const totalAppliedValueCell = CostUI.createCostCell(appliedValue);
totalAppliedValueCell.classList.add("clickable-cell"); totalAppliedValueCell.classList.add("clickable-cell");
const totalCostCell = CostUI.createTotalCostCell(costItem);
const totalCostCell = CostUI.createTotalCostCell(
costItem,
callbacks.addSumCostValue
);
const actionsCell = CostUI.costItemActions(costItem, callbacks); const actionsCell = CostUI.costItemActions(costItem, callbacks);
actionsCell.classList.add("actions-column"); actionsCell.classList.add("actions-column");
@@ -630,15 +644,37 @@ export class CostUI {
return cell; return cell;
} }
static createTotalCostCell(costItem) { static createCostCell(appliedValue) {
return CostUI.createTableCell(appliedValue);
}
static createTotalCostCell(costItem, callback) {
const totalCostCell = document.createElement("td"); const totalCostCell = document.createElement("td");
const totalCost = parseFloat(costItem.TotalCost).toFixed(2); const totalCost = CostUI.format_number(costItem.TotalCost);
totalCostCell.textContent = costItem.is_sum totalCostCell.textContent = totalCost;
? totalCost + " (Σ)" if (costItem.is_sum) {
: totalCost; totalCostCell.textContent = totalCost + " *";
} else if (!costItem.TotalCost) {
const button = CostUI.createButton("Sum", "fa-solid fa-calculator");
button.addEventListener("click", function () {
const costItemId = parseInt(this.closest("tr").getAttribute("id"));
callback(costItemId);
});
const onHoverElement = CostUI.onHoverElement();
onHoverElement.appendChild(button);
totalCostCell.appendChild(onHoverElement);
}
return totalCostCell; return totalCostCell;
} }
static onHoverElement() {
const div = document.createElement("div");
div.classList.add("actions-column");
div.classList.add("append-right");
return div;
}
static costItemActions(costItem, callbacks) { static costItemActions(costItem, callbacks) {
const divFlex = document.createElement("div"); const divFlex = document.createElement("div");
divFlex.classList.add("row-container"); divFlex.classList.add("row-container");
@@ -779,9 +815,7 @@ export class CostUI {
); );
cardTitle.classList.add("card-title"); cardTitle.classList.add("card-title");
const cardButton = document.createElement("button"); const cardButton = CostUI.createButton("Load", "fa-solid fa-repeat");
cardButton.classList.add("action-button");
cardButton.textContent = "Load";
cardButton.addEventListener("click", callback); cardButton.addEventListener("click", callback);
cardBody.appendChild(cardTitle); cardBody.appendChild(cardTitle);
@@ -912,8 +946,13 @@ export class CostUI {
costType = "FIXED"; costType = "FIXED";
} }
const options = ["FIXED", "CATEGORY", "SUM"]; const options = ["FIXED", "CATEGORY", "SUM"];
const typeCell = CostUI.createTableDropdown("type", options, costType); const typeCell = document.createElement("td");
const dropdown = typeCell.querySelector("select"); const dropdown = CostUI.createTableDropdown({
name: "type",
options: options,
defaultValue: costType,
});
typeCell.appendChild(dropdown);
dropdown.addEventListener("change", function () { dropdown.addEventListener("change", function () {
const selectedType = this.value; const selectedType = this.value;
CostUI.updateRowBasedOnType(selectedType, categoryCell, valueCell1); CostUI.updateRowBasedOnType(selectedType, categoryCell, valueCell1);
@@ -982,16 +1021,16 @@ export class CostUI {
handleCostValueChange.call(this, costItemId); handleCostValueChange.call(this, costItemId);
} }
}); });
const deleteCell = document.createElement("td"); const deleteCell = document.createElement("td");
tr.appendChild(deleteCell); tr.appendChild(deleteCell);
const deleteCostValue = document.createElement("button"); const dleteButton = CostUI.createButton("Delete", "fa-solid fa-trash");
deleteCostValue.classList.add("action-button"); dleteButton.addEventListener("click", function () {
deleteCostValue.textContent = "Delete";
deleteCostValue.addEventListener("click", function () {
costValueCallbacks.deleteCostValue(costItemId, costValue.id); costValueCallbacks.deleteCostValue(costItemId, costValue.id);
tr.remove(); tr.remove();
}); });
deleteCell.appendChild(deleteCostValue);
deleteCell.appendChild(dleteButton);
CostUI.updateRowBasedOnType(costType, categoryCell, valueCell1); CostUI.updateRowBasedOnType(costType, categoryCell, valueCell1);
return tr; return tr;
} }
@@ -1015,23 +1054,19 @@ export class CostUI {
} }
} }
static createTableDropdown(name, options, value = "FIXED") { static createTableDropdown({ name, options, defaultValue = "FIXED" }) {
const cell = document.createElement("td");
const dropdown = document.createElement("select"); const dropdown = document.createElement("select");
dropdown.name = name; dropdown.name = name;
options.forEach((optionValue) => { options.forEach((optionValue) => {
const option = document.createElement("option"); const option = document.createElement("option");
option.value = optionValue; option.value = optionValue;
option.textContent = optionValue; option.textContent = optionValue;
if (optionValue === value) { if (optionValue === defaultValue) {
option.selected = true; option.selected = true;
} }
dropdown.appendChild(option); dropdown.appendChild(option);
}); });
return dropdown;
cell.appendChild(dropdown);
return cell;
} }
static createTableInput(type, name, value) { static createTableInput(type, name, value) {
@@ -1045,9 +1080,7 @@ export class CostUI {
} }
static createAddCostValueButton(costItemId, callbacks) { static createAddCostValueButton(costItemId, callbacks) {
const addButton = document.createElement("button"); const addButton = CostUI.createButton("Add Cost Value", "fa-solid fa-plus");
addButton.classList.add("action-button");
addButton.textContent = " + Add Cost Value";
addButton.addEventListener("click", function (e) { addButton.addEventListener("click", function (e) {
e.preventDefault(); e.preventDefault();
callbacks.addCostValue ? callbacks.addCostValue(costItemId) : null; callbacks.addCostValue ? callbacks.addCostValue(costItemId) : null;
@@ -1992,7 +2025,6 @@ export class CostUI {
); );
manualQuantitiesSection.appendChild(text); manualQuantitiesSection.appendChild(text);
} else { } else {
// add dropdown to chose quantity type , from "IfcQuantityArea", "IfcQuantityLength", "IfcQuantityVolume", "IfcQuantityCount", "IfcQuantityWeight "
const quantityTypes = [ const quantityTypes = [
"IfcQuantityArea", "IfcQuantityArea",
"IfcQuantityLength", "IfcQuantityLength",
@@ -2000,18 +2032,15 @@ export class CostUI {
"IfcQuantityCount", "IfcQuantityCount",
"IfcQuantityWeight", "IfcQuantityWeight",
]; ];
const dropdown = CostUI.createTableDropdown( const dropdown = CostUI.createTableDropdown({
"type", name: "type",
quantityTypes, options: quantityTypes,
"IfcQuantityArea" defaultValue: "IfcQuantityArea",
); });
dropdown.id = "quantity-type-dropdown-" + costItemId; dropdown.id = "quantity-type-dropdown-" + costItemId;
manualQuantitiesSection.appendChild(dropdown); manualQuantitiesSection.appendChild(dropdown);
} }
// if quantity is ty IfcQuantityCount, and the costQuantities
if (quantityType === "IfcQuantityCount" && has_assigned_products) { if (quantityType === "IfcQuantityCount" && has_assigned_products) {
// write text that one of the quantities is assigned to the product selection
const text = CostUI.Text( const text = CostUI.Text(
" One of the quantities is assigned to the product selection", " One of the quantities is assigned to the product selection",
"fa-solid fa-warning", "fa-solid fa-warning",
@@ -2060,7 +2089,6 @@ export class CostUI {
); );
manualQuantitiesSection.appendChild(tableContainer); manualQuantitiesSection.appendChild(tableContainer);
// manualQuantitiesSection.appendChild(tableContainer2);
manualQuantitiesSection.appendChild(addButton); manualQuantitiesSection.appendChild(addButton);
return manualQuantitiesSection; return manualQuantitiesSection;
} }
@@ -2068,14 +2096,9 @@ export class CostUI {
static createManualQuantityRow(costItemId, quantity, callbacks) { static createManualQuantityRow(costItemId, quantity, callbacks) {
const tr = document.createElement("tr"); const tr = document.createElement("tr");
tr.id = quantity.id; tr.id = quantity.id;
// get quantity keys and values to create the table row cells
// Label cell
Object.entries(quantity).forEach(([key, value]) => { Object.entries(quantity).forEach(([key, value]) => {
if (key !== "id" && key !== "fromProduct") { if (key !== "id" && key !== "fromProduct") {
if (key === "Name") { if (key === "Name") {
// create input
const cell = document.createElement("td"); const cell = document.createElement("td");
const input = document.createElement("input"); const input = document.createElement("input");
input.type = "text"; input.type = "text";
@@ -2093,7 +2116,6 @@ export class CostUI {
}); });
} }
// if key contains value
else if (key.toLowerCase().includes("value")) { else if (key.toLowerCase().includes("value")) {
const cell = document.createElement("td"); const cell = document.createElement("td");
const input = document.createElement("input"); const input = document.createElement("input");
@@ -2105,7 +2127,7 @@ export class CostUI {
if (e.key === "Enter") { if (e.key === "Enter") {
e.preventDefault(); e.preventDefault();
callbacks.editQuantity(costItemId, quantity.id, { callbacks.editQuantity(costItemId, quantity.id, {
[key]: parseFloat(input.value), // Dynamically set the key and parse the value as a double [key]: parseFloat(input.value),
}); });
} }
}); });
@@ -2118,7 +2140,6 @@ export class CostUI {
} }
}); });
// Add delete button
const deleteButton = CostUI.createButton("Delete", "fa-solid fa-trash"); const deleteButton = CostUI.createButton("Delete", "fa-solid fa-trash");
const deleteCell = document.createElement("td"); const deleteCell = document.createElement("td");
deleteButton.addEventListener("click", function (e) { deleteButton.addEventListener("click", function (e) {
@@ -2142,7 +2163,6 @@ export class CostUI {
e.preventDefault(); e.preventDefault();
let type = quantityType; let type = quantityType;
if (!type) { if (!type) {
// get quantityType from dropdown
const qtoSection = document.getElementById( const qtoSection = document.getElementById(
"manual-quantities-section-" + costItemId "manual-quantities-section-" + costItemId
); );
+11
View File
@@ -420,6 +420,17 @@ class Web(bonsai.core.tool.Web):
data_key="cost_value", data_key="cost_value",
event="cost_value", event="cost_value",
) )
if operator_data["type"] == "addSumCostValue":
cost_item = ifc_file.by_id(operator_data["costItemId"])
value = ifcopenshell.api.cost.add_cost_value(
ifc_file,
parent=cost_item,
)
ifcopenshell.api.cost.edit_cost_value(file=ifc_file, cost_value=value, attributes={"Category": "*"})
cost_schedule = tool.Cost.get_cost_schedule(cost_item=cost_item)
tool.Cost.load_cost_schedule_tree()
cls.load_cost_schedule_web_ui(cost_schedule)
if operator_data["type"] == "deleteCostValue": if operator_data["type"] == "deleteCostValue":
bpy.ops.bim.remove_cost_value(parent=operator_data["costItemId"], cost_value=operator_data["costValueId"]) bpy.ops.bim.remove_cost_value(parent=operator_data["costItemId"], cost_value=operator_data["costValueId"])
cost_item = ifc_file.by_id(operator_data["costItemId"]) cost_item = ifc_file.by_id(operator_data["costItemId"])