mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Cost module web UI:
- quick button to add a SUM cost value - style improvements
This commit is contained in:
@@ -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
|
||||
);
|
||||
|
||||
@@ -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"])
|
||||
|
||||
Reference in New Issue
Block a user