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