mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 15:08:51 +00:00
cost module web ui improvements:
-add ribbon bar, - improve settings menu with ability to change font size for tables. - Improve cost schedule spreadsheet layout
This commit is contained in:
@@ -25,18 +25,18 @@
|
|||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
:root.blender .card-body {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root.blender .card-title {
|
:root.blender .card-title {
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root.blender .card-text {
|
:root.blender .card-text {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root.blender .btn-primary {
|
:root.blender .btn-primary {
|
||||||
|
|||||||
@@ -1,36 +1,72 @@
|
|||||||
table {
|
:root {
|
||||||
width: 100%;
|
--fontSize: 14px;
|
||||||
height: 80%;
|
}
|
||||||
border-collapse: collapse;
|
body {
|
||||||
user-select: none;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-container {
|
.table-container {
|
||||||
max-height: 70vh;
|
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
form table {
|
#cost-items {
|
||||||
height: 50%;
|
height: calc(100vh - var(--margin-small));
|
||||||
}
|
}
|
||||||
|
|
||||||
form .table-container {
|
|
||||||
max-height: 50vh;
|
.table-scrollable-body {
|
||||||
|
max-height: 70vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
user-select: none;
|
||||||
|
table-layout: fixed;
|
||||||
|
font-size: var(--fontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
thead {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
background-color: #727272;
|
||||||
|
font-size: larger;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
td,
|
td,
|
||||||
th {
|
th {
|
||||||
border: 1px solid #ddd;
|
padding: 0;
|
||||||
padding: 8px;
|
margin: 0;
|
||||||
text-align: left;
|
border-left: 1px solid #ddd;
|
||||||
|
border-right: 1px solid #ddd;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 0;
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
box-sizing: border-box; /* Include padding and border in the element's total width and height */
|
||||||
|
}
|
||||||
|
|
||||||
/* first td of a row, must be flex */
|
/* first td of a row, must be flex */
|
||||||
td:first-child {
|
td:first-child {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -65,7 +101,7 @@ select {
|
|||||||
height: inherit;
|
height: inherit;
|
||||||
margin-left: 0.5em;
|
margin-left: 0.5em;
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
|
font-size: var(--fontSize);
|
||||||
min-width: 50px; /* Minimum width to prevent it from becoming too small */
|
min-width: 50px; /* Minimum width to prevent it from becoming too small */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +137,7 @@ input:focus {
|
|||||||
background-color: #444444;
|
background-color: #444444;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive Styles */
|
/* Responsive Styles */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
table {
|
table {
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
.ribbon-bar {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border: 1px solid #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ribbon-bar .action-button {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border: none;
|
||||||
|
background-color: #444;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
border-left: 1px solid #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ribbon-bar .action-button:hover {
|
||||||
|
background-color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ribbon-bar .action-button::after {
|
||||||
|
content: attr(data-tooltip);
|
||||||
|
position: absolute;
|
||||||
|
bottom: -30px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background-color: #333;
|
||||||
|
color: #fff;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
white-space: nowrap;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ribbon-bar .action-button:hover::after {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
@import url("./components/card.css");
|
@import url("./components/card.css");
|
||||||
@import url("./components/contextMenu.css");
|
@import url("./components/contextMenu.css");
|
||||||
@import url("./components/costTable.css");
|
@import url("./components/costTable.css");
|
||||||
|
@import url("./components/headerBar.css");
|
||||||
|
|
||||||
.form-header {
|
.form-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -19,8 +20,8 @@ span {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.floating-form {
|
.floating-form {
|
||||||
min-width: 40%;
|
height: 40vh;
|
||||||
max-width: 80%;
|
width: 50vw;
|
||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
height: auto;
|
height: auto;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -219,7 +219,6 @@ button:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.row-container {
|
.row-container {
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|||||||
@@ -11,21 +11,22 @@ $(document).ready(function () {
|
|||||||
setTheme(theme);
|
setTheme(theme);
|
||||||
|
|
||||||
connectSocket();
|
connectSocket();
|
||||||
|
CostUI.addSettingsMenu();
|
||||||
const settingsMenu = CostUI.Form({
|
CostUI.addRibbonButton({
|
||||||
id: "settings-menu",
|
text: "Hide Schedules",
|
||||||
name: "Settings",
|
icon: "fa-regular fa-eye-slash",
|
||||||
icon: "fa-solid fa-gear",
|
callback: (button) => {
|
||||||
shouldHide: true,
|
CostUI.toggleSchedulesContainer(button);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
settingsMenu.style.display = "none";
|
CostUI.addRibbonButton({
|
||||||
const picker = CostUI.createColorPicker();
|
text: "Settings",
|
||||||
settingsMenu.appendChild(picker);
|
icon: "fas fa-cog",
|
||||||
|
callback: (button) => {
|
||||||
const settingsButton = document.getElementById("settings-button");
|
CostUI.toggleSettingsMenu(button);
|
||||||
settingsButton.addEventListener("click", function () {
|
}
|
||||||
settingsMenu.style.display = "block";
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function connectSocket() {
|
function connectSocket() {
|
||||||
@@ -85,6 +86,7 @@ function handleSelectedProducts(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handlePredefinedTypes(data) {
|
function handlePredefinedTypes(data) {
|
||||||
|
|
||||||
CostUI.enableAddingCostSchedule(
|
CostUI.enableAddingCostSchedule(
|
||||||
data.data["predefined_types"],
|
data.data["predefined_types"],
|
||||||
addCostSchedule
|
addCostSchedule
|
||||||
@@ -233,6 +235,65 @@ function setTheme(theme) {
|
|||||||
localStorage.setItem("theme", theme);
|
localStorage.setItem("theme", theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleClientList() {
|
||||||
|
var clientList = $("#client-list");
|
||||||
|
|
||||||
|
if (clientList.hasClass("show")) {
|
||||||
|
clientList.removeClass("show");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clientList.empty();
|
||||||
|
var counter = 0;
|
||||||
|
|
||||||
|
$.each(connectedClients, function (id, client) {
|
||||||
|
counter++;
|
||||||
|
|
||||||
|
const dropdownIcon = $("<i>")
|
||||||
|
.addClass("fas fa-chevron-down")
|
||||||
|
.css("margin-left", "0.5rem");
|
||||||
|
|
||||||
|
const clientDiv = $("<div>")
|
||||||
|
.addClass("client")
|
||||||
|
.text(client.ifc_file || "Blender " + counter);
|
||||||
|
|
||||||
|
clientDiv.append(dropdownIcon);
|
||||||
|
|
||||||
|
const clientDetailsDiv = $("<div>").addClass("client-details");
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
const clientId = $("<div>")
|
||||||
|
.addClass("client-detail")
|
||||||
|
.text(`Blender ID: ${id}`);
|
||||||
|
clientDetailsDiv.append(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client.has_drawings) {
|
||||||
|
const clientShown = $("<div>")
|
||||||
|
.addClass("client-detail")
|
||||||
|
.text("No drawings exist for this IFC File yet");
|
||||||
|
clientDetailsDiv.append(clientShown);
|
||||||
|
} else {
|
||||||
|
const clientNumbers = $("<div>")
|
||||||
|
.addClass("client-detail")
|
||||||
|
.text(
|
||||||
|
`${allDrawings[client.ifc_file].drawings.length} Drawing(s),
|
||||||
|
${allDrawings[client.ifc_file].sheets.length} Sheet(s)`
|
||||||
|
);
|
||||||
|
clientDetailsDiv.append(clientNumbers);
|
||||||
|
}
|
||||||
|
|
||||||
|
clientDiv.append(clientDetailsDiv);
|
||||||
|
|
||||||
|
clientDiv.on("click", function () {
|
||||||
|
clientDetailsDiv.toggleClass("show");
|
||||||
|
});
|
||||||
|
|
||||||
|
clientList.append(clientDiv);
|
||||||
|
});
|
||||||
|
clientList.addClass("show");
|
||||||
|
}
|
||||||
|
|
||||||
function addCostItem(costItemId) {
|
function addCostItem(costItemId) {
|
||||||
executeOperator({ type: "addCostItem", costItemId: costItemId });
|
executeOperator({ type: "addCostItem", costItemId: costItemId });
|
||||||
}
|
}
|
||||||
@@ -304,6 +365,7 @@ function handleCostSchedulesData(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleCostItemsData(data) {
|
function handleCostItemsData(data) {
|
||||||
|
console.log(data);
|
||||||
const cards = document.querySelectorAll("[id^='schedule-']");
|
const cards = document.querySelectorAll("[id^='schedule-']");
|
||||||
cards.forEach((card) => {
|
cards.forEach((card) => {
|
||||||
if (card.id === "schedule-" + data.data["cost_items"]["cost_schedule_id"]) {
|
if (card.id === "schedule-" + data.data["cost_items"]["cost_schedule_id"]) {
|
||||||
|
|||||||
@@ -126,7 +126,10 @@ function handleWorkScheduleData(data) {
|
|||||||
const workSchedules = data["data"]["work_schedule_info"];
|
const workSchedules = data["data"]["work_schedule_info"];
|
||||||
|
|
||||||
workSchedules.forEach((workSchedule) => {
|
workSchedules.forEach((workSchedule) => {
|
||||||
const mainContainer = CostUI.text(new Date(workSchedule.CreationDate).toLocaleDateString());
|
const mainContainer = CostUI.Text(
|
||||||
|
"Created On: " + new Date(workSchedule.CreationDate).toLocaleDateString(),
|
||||||
|
"fa-solid fa-pen-nib"
|
||||||
|
);
|
||||||
const callback = () => loadWorkSchedule(workSchedule.id);
|
const callback = () => loadWorkSchedule(workSchedule.id);
|
||||||
const card = CostUI.createCard(workSchedule.id, workSchedule.Name,mainContainer, callback);
|
const card = CostUI.createCard(workSchedule.id, workSchedule.Name,mainContainer, callback);
|
||||||
workSchedulesDiv.append(card);
|
workSchedulesDiv.append(card);
|
||||||
|
|||||||
@@ -25,7 +25,10 @@ export class CostUI {
|
|||||||
"Total Cost",
|
"Total Cost",
|
||||||
"Action",
|
"Action",
|
||||||
];
|
];
|
||||||
|
const thead = document.createElement("thead");
|
||||||
const tr = document.createElement("tr");
|
const tr = document.createElement("tr");
|
||||||
|
thead.appendChild(tr);
|
||||||
|
|
||||||
|
|
||||||
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");
|
||||||
@@ -41,7 +44,7 @@ export class CostUI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody.appendChild(tr);
|
table.appendChild(thead);
|
||||||
table.appendChild(tbody);
|
table.appendChild(tbody);
|
||||||
document.getElementById("cost-items").appendChild(table);
|
document.getElementById("cost-items").appendChild(table);
|
||||||
|
|
||||||
@@ -69,7 +72,8 @@ export class CostUI {
|
|||||||
`;
|
`;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
}
|
}
|
||||||
static createContextMenu(callbacks) {
|
|
||||||
|
static createContextMenu(callbacks) {
|
||||||
const contextMenu = document.createElement("div");
|
const contextMenu = document.createElement("div");
|
||||||
contextMenu.id = "context-menu";
|
contextMenu.id = "context-menu";
|
||||||
contextMenu.classList.add("context-menu");
|
contextMenu.classList.add("context-menu");
|
||||||
@@ -144,7 +148,7 @@ export class CostUI {
|
|||||||
const costItemId = parseInt(targetRow.getAttribute("id"));
|
const costItemId = parseInt(targetRow.getAttribute("id"));
|
||||||
let expandedState =
|
let expandedState =
|
||||||
JSON.parse(localStorage.getItem("expandedState")) || {};
|
JSON.parse(localStorage.getItem("expandedState")) || {};
|
||||||
expandedState = CostUI.deleteRow(targetRow, expandedState);
|
expandedState = CostUI.deleteCostItemRow(targetRow, expandedState);
|
||||||
localStorage.setItem("expandedState", JSON.stringify(expandedState));
|
localStorage.setItem("expandedState", JSON.stringify(expandedState));
|
||||||
callbacks.deleteCostItem(costItemId);
|
callbacks.deleteCostItem(costItemId);
|
||||||
}
|
}
|
||||||
@@ -178,9 +182,51 @@ export class CostUI {
|
|||||||
});
|
});
|
||||||
getSelectedProducts.hasListener = true;
|
getSelectedProducts.hasListener = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to get column names from the table header
|
||||||
|
function getColumnNames(tableId) {
|
||||||
|
const table = document.getElementById(tableId);
|
||||||
|
const headerRow = table.querySelector("thead tr");
|
||||||
|
return Array.from(headerRow.children).map(headerCell => headerCell.textContent.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add double-click event listener
|
||||||
|
document.getElementById("cost-items").addEventListener("dblclick", function (event) {
|
||||||
|
const targetRow = event.target.closest("tr");
|
||||||
|
const targetCell = event.target.closest("td");
|
||||||
|
if (targetRow && targetCell) {
|
||||||
|
const columnIndex = Array.from(targetRow.children).indexOf(targetCell);
|
||||||
|
const costItemId = parseInt(targetRow.getAttribute("id"));
|
||||||
|
const cellContent = targetCell.textContent;
|
||||||
|
|
||||||
|
// Get column names from the table header
|
||||||
|
const columnNames = getColumnNames("cost-items");
|
||||||
|
const cellName = columnNames[columnIndex];
|
||||||
|
|
||||||
|
console.log("Column Name: ", cellName);
|
||||||
|
console.log("Content: ", cellContent);
|
||||||
|
|
||||||
|
// Implement different behaviors based on the column index
|
||||||
|
switch (columnIndex) {
|
||||||
|
case 0:
|
||||||
|
// Behavior for the first column
|
||||||
|
callbacks.firstColumnDoubleClick ? callbacks.firstColumnDoubleClick(costItemId) : null;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
// Behavior for the second column
|
||||||
|
callbacks.secondColumnDoubleClick ? callbacks.secondColumnDoubleClick(costItemId) : null;
|
||||||
|
break;
|
||||||
|
// Add more cases as needed for other columns
|
||||||
|
default:
|
||||||
|
// Default behavior
|
||||||
|
callbacks.defaultDoubleClick ? callbacks.defaultDoubleClick(costItemId) : null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static deleteRow(targetRow, expandedState) {
|
static deleteCostItemRow(targetRow, expandedState) {
|
||||||
if (!targetRow) {
|
if (!targetRow) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -188,7 +234,7 @@ export class CostUI {
|
|||||||
targetRow.remove();
|
targetRow.remove();
|
||||||
const subRows = document.querySelectorAll(`[parent-id='${targetRow.id}']`);
|
const subRows = document.querySelectorAll(`[parent-id='${targetRow.id}']`);
|
||||||
subRows.forEach((subRow) => {
|
subRows.forEach((subRow) => {
|
||||||
CostUI.deleteRow(subRow, expandedState);
|
CostUI.deleteCostItemRow(subRow, expandedState);
|
||||||
});
|
});
|
||||||
return expandedState;
|
return expandedState;
|
||||||
}
|
}
|
||||||
@@ -258,6 +304,7 @@ export class CostUI {
|
|||||||
const form = CostUI.Form({
|
const form = CostUI.Form({
|
||||||
id: "add-cost-schedule-form",
|
id: "add-cost-schedule-form",
|
||||||
name: name,
|
name: name,
|
||||||
|
icon: "fa-solid fa-money-bill-wave",
|
||||||
});
|
});
|
||||||
|
|
||||||
const nameLabel = document.createElement("label");
|
const nameLabel = document.createElement("label");
|
||||||
@@ -672,9 +719,8 @@ export class CostUI {
|
|||||||
const cardBody = document.createElement("div");
|
const cardBody = document.createElement("div");
|
||||||
cardBody.classList.add("card-body");
|
cardBody.classList.add("card-body");
|
||||||
|
|
||||||
const cardTitle = document.createElement("h5");
|
const cardTitle = CostUI.Text(title, "fa-solid fa-money-bill-wave", "large");
|
||||||
cardTitle.classList.add("card-title");
|
cardTitle.classList.add("card-title");
|
||||||
cardTitle.textContent = title;
|
|
||||||
|
|
||||||
const cardButton = document.createElement("button");
|
const cardButton = document.createElement("button");
|
||||||
cardButton.classList.add("action-button");
|
cardButton.classList.add("action-button");
|
||||||
@@ -1163,10 +1209,10 @@ export class CostUI {
|
|||||||
const subtotalRow = document.createElement("tr");
|
const subtotalRow = document.createElement("tr");
|
||||||
|
|
||||||
const cells = [
|
const cells = [
|
||||||
|
{ textContent: "", style: { border: "none" } },
|
||||||
|
{ textContent: "", style: { border: "none" } },
|
||||||
|
{ textContent: "", style: { border: "none" } },
|
||||||
{ textContent: "Subtotal", colSpan: 4, style: { border: "none" } },
|
{ textContent: "Subtotal", colSpan: 4, style: { border: "none" } },
|
||||||
{ textContent: "", style: { border: "none" } },
|
|
||||||
{ textContent: "", style: { border: "none" } },
|
|
||||||
{ textContent: "", style: { border: "none" } },
|
|
||||||
{ textContent: quantitySums.count },
|
{ textContent: quantitySums.count },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -1202,15 +1248,25 @@ export class CostUI {
|
|||||||
costItemId,
|
costItemId,
|
||||||
callbacks,
|
callbacks,
|
||||||
}) {
|
}) {
|
||||||
|
if (products.length === 0) {
|
||||||
|
const noProductsMessage = document.createElement("p");
|
||||||
|
noProductsMessage.textContent = "Your Blender Selection is empty! Select objects first.";
|
||||||
|
form.appendChild(noProductsMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const { tableContainer, table } = CostUI.addTable({
|
const { tableContainer, table } = CostUI.addTable({
|
||||||
headers: ["Step Id", "Class", "Name", "Type", "Count"],
|
headers: ["Step Id", "Class", "Name", "Type", "Count"],
|
||||||
className: "product-table",
|
className: "product-table",
|
||||||
id: "cost-values-table-" + costItemId,
|
id: "cost-values-table-" + costItemId,
|
||||||
});
|
});
|
||||||
|
form.appendChild(tableContainer);
|
||||||
|
|
||||||
|
const scrollableBody = document.createElement("div");
|
||||||
|
scrollableBody.classList.add("table-scrollable-body");
|
||||||
|
|
||||||
const tbody = document.createElement("tbody");
|
const tbody = document.createElement("tbody");
|
||||||
table.appendChild(tbody);
|
scrollableBody.appendChild(tbody);
|
||||||
form.appendChild(tableContainer);
|
table.appendChild(scrollableBody);
|
||||||
|
|
||||||
const quantityAssigned = CostUI.getAssinedQuantity(products, costItemId);
|
const quantityAssigned = CostUI.getAssinedQuantity(products, costItemId);
|
||||||
const areProductsSameClass = products.every(
|
const areProductsSameClass = products.every(
|
||||||
@@ -1343,9 +1399,12 @@ export class CostUI {
|
|||||||
id: "cost-values-table-" + costItemId,
|
id: "cost-values-table-" + costItemId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const tbody = document.createElement("tbody");
|
|
||||||
table.appendChild(tbody);
|
|
||||||
form.appendChild(tableContainer);
|
form.appendChild(tableContainer);
|
||||||
|
const scrollableBody = document.createElement("div");
|
||||||
|
scrollableBody.classList.add("table-scrollable-body");
|
||||||
|
const tbody = document.createElement("tbody");
|
||||||
|
scrollableBody.appendChild(tbody);
|
||||||
|
table.appendChild(scrollableBody);
|
||||||
|
|
||||||
let quantityAssigned = CostUI.getAssinedQuantity(products, costItemId);
|
let quantityAssigned = CostUI.getAssinedQuantity(products, costItemId);
|
||||||
const shouldAddQto = quantityAssigned ? true : false;
|
const shouldAddQto = quantityAssigned ? true : false;
|
||||||
@@ -1417,4 +1476,174 @@ export class CostUI {
|
|||||||
addProductAssignmentsButton.classList.add("action-button");
|
addProductAssignmentsButton.classList.add("action-button");
|
||||||
return addProductAssignmentsButton;
|
return addProductAssignmentsButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static addSettingsMenu() {
|
||||||
|
if (document.getElementById("settings-menu")) {
|
||||||
|
return document.getElementById("settings-menu");
|
||||||
|
}
|
||||||
|
const settingsMenu = CostUI.Form({
|
||||||
|
id: "settings-menu",
|
||||||
|
name: "Settings",
|
||||||
|
icon: "fa-solid fa-gear",
|
||||||
|
shouldHide: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const picker = CostUI.createColorPicker();
|
||||||
|
const tableFontSize = CostUI.createFontSizePicker();
|
||||||
|
settingsMenu.appendChild(picker);
|
||||||
|
settingsMenu.appendChild(tableFontSize);
|
||||||
|
|
||||||
|
settingsMenu.style.display = "none";
|
||||||
|
const settingsButton = document.getElementById("settings-button");
|
||||||
|
settingsButton.addEventListener("click", function () {
|
||||||
|
settingsMenu.style.display = "block";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply saved settings on page load
|
||||||
|
CostUI.applySavedSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
static createFontSizePicker() {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
const fontSizeText = document.createElement("p");
|
||||||
|
fontSizeText.textContent = "Select a font size for the table:";
|
||||||
|
div.appendChild(fontSizeText);
|
||||||
|
const fontSizePicker = document.createElement("input");
|
||||||
|
fontSizePicker.type = "number";
|
||||||
|
fontSizePicker.id = "font-size-picker";
|
||||||
|
fontSizePicker.value = localStorage.getItem("tableFontSize") || 14;
|
||||||
|
div.appendChild(fontSizePicker);
|
||||||
|
fontSizePicker.addEventListener("input", function () {
|
||||||
|
const fontSize = fontSizePicker.value + "px";
|
||||||
|
CostUI.saveFontSize(fontSize);
|
||||||
|
CostUI.applyFontSizeToAllTables(fontSize);
|
||||||
|
});
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
|
static createColorPicker() {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
const colorText = document.createElement("p");
|
||||||
|
colorText.textContent = "Select a color for the table:";
|
||||||
|
div.appendChild(colorText);
|
||||||
|
const colorPicker = document.createElement("input");
|
||||||
|
colorPicker.type = "color";
|
||||||
|
colorPicker.id = "color-picker";
|
||||||
|
colorPicker.value = localStorage.getItem("tableColor") || "#ffffff";
|
||||||
|
div.appendChild(colorPicker);
|
||||||
|
colorPicker.addEventListener("input", function () {
|
||||||
|
const color = colorPicker.value;
|
||||||
|
CostUI.saveColor(color);
|
||||||
|
CostUI.applyColorToAllTables(color);
|
||||||
|
});
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
|
static saveFontSize(fontSize) {
|
||||||
|
localStorage.setItem("tableFontSize", fontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static saveColor(color) {
|
||||||
|
localStorage.setItem("tableColor", color);
|
||||||
|
}
|
||||||
|
|
||||||
|
static applyFontSizeToAllTables(fontSize) {
|
||||||
|
document.documentElement.style.setProperty('--fontSize', fontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static applyColorToAllTables(color) {
|
||||||
|
const tables = document.querySelectorAll("table");
|
||||||
|
tables.forEach((table) => {
|
||||||
|
table.style.backgroundColor = color;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static applySavedSettings() {
|
||||||
|
console.log("Applying saved settings");
|
||||||
|
const savedFontSize = localStorage.getItem("tableFontSize");
|
||||||
|
if (savedFontSize) {
|
||||||
|
CostUI.applyFontSizeToAllTables(savedFontSize);
|
||||||
|
const fontSizePicker = document.getElementById("font-size-picker");
|
||||||
|
if (fontSizePicker) {
|
||||||
|
fontSizePicker.value = parseInt(savedFontSize, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedColor = localStorage.getItem("tableColor");
|
||||||
|
if (savedColor) {
|
||||||
|
CostUI.applyColorToAllTables(savedColor);
|
||||||
|
const colorPicker = document.getElementById("color-picker");
|
||||||
|
if (colorPicker) {
|
||||||
|
colorPicker.value = savedColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static getRibbonBar() {
|
||||||
|
return document.getElementById("ribbon-bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
static addRibbonButton({ text, icon, callback }) {
|
||||||
|
const ribbonBar = CostUI.getRibbonBar();
|
||||||
|
|
||||||
|
const button = document.createElement("button");
|
||||||
|
button.classList.add("action-button", ...icon.split(" "));
|
||||||
|
button.dataset.tooltip = text; // Use dataset to set the tooltip attribute
|
||||||
|
|
||||||
|
button.addEventListener("click", () => callback(button));
|
||||||
|
|
||||||
|
ribbonBar.appendChild(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
static toggleSchedulesContainer(button) {
|
||||||
|
const schedulesContainer = CostUI.getSchedulesContainer();
|
||||||
|
const isHidden = schedulesContainer.style.display === "none";
|
||||||
|
|
||||||
|
if (isHidden) {
|
||||||
|
CostUI.displaySchedulesContainer();
|
||||||
|
button.classList.remove("fa-eye");
|
||||||
|
button.classList.add("fa-eye-slash");
|
||||||
|
button.dataset.tooltip = "Hide Schedules";
|
||||||
|
} else {
|
||||||
|
CostUI.hideSchedulesContainer();
|
||||||
|
button.classList.remove("fa-eye-slash");
|
||||||
|
button.classList.add("fa-eye");
|
||||||
|
button.dataset.tooltip = "Display Schedules";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSchedulesContainer() {
|
||||||
|
return document.getElementById("cost-schedules");
|
||||||
|
}
|
||||||
|
|
||||||
|
static displaySchedulesContainer() {
|
||||||
|
const schedulesContainer = CostUI.getSchedulesContainer();
|
||||||
|
schedulesContainer.style.display = "flex";
|
||||||
|
}
|
||||||
|
|
||||||
|
static hideSchedulesContainer() {
|
||||||
|
const schedulesContainer = CostUI.getSchedulesContainer();
|
||||||
|
schedulesContainer.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSettingsMenu() {
|
||||||
|
return document.getElementById("settings-menu");
|
||||||
|
}
|
||||||
|
|
||||||
|
static toggleSettingsMenu(button) {
|
||||||
|
const settingsMenu = CostUI.getSettingsMenu();
|
||||||
|
const isHidden = settingsMenu.style.display === "none";
|
||||||
|
|
||||||
|
if (isHidden) {
|
||||||
|
settingsMenu.style.display = "block";
|
||||||
|
button.classList.remove("fa-gear");
|
||||||
|
button.classList.add("fa-times");
|
||||||
|
button.dataset.tooltip = "Close Settings";
|
||||||
|
} else {
|
||||||
|
settingsMenu.style.display = "none";
|
||||||
|
button.classList.remove("fa-times");
|
||||||
|
button.classList.add("fa-gear");
|
||||||
|
button.dataset.tooltip = "Open Settings";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,12 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>BlenderBIM Web UI</title>
|
<title>BlenderBIM Web UI</title>
|
||||||
<link rel="stylesheet" type="text/css" href="/static/css/cost.css" id="index-stylesheet" />
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
href="/static/css/cost.css"
|
||||||
|
id="index-stylesheet"
|
||||||
|
/>
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
id="tabulator-stylesheet"
|
id="tabulator-stylesheet"
|
||||||
@@ -30,9 +35,6 @@
|
|||||||
var SOCKET_PORT = {{port}};
|
var SOCKET_PORT = {{port}};
|
||||||
</script>
|
</script>
|
||||||
<script type="module" defer src="./static/js/cost.js"></script>
|
<script type="module" defer src="./static/js/cost.js"></script>
|
||||||
<style>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav>
|
<nav>
|
||||||
@@ -43,12 +45,10 @@
|
|||||||
/>
|
/>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="/"
|
<a href="/"><i class="fa-solid fa-table"></i> Schedules</a>
|
||||||
><i class="fa-solid fa-table"></i> Schedules</a
|
|
||||||
>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/costing" class="active"
|
<a href="/costing" class="active"
|
||||||
><i class="fa-solid fa-money-bill-trend-up"></i> Costing</a
|
><i class="fa-solid fa-money-bill-trend-up"></i> Costing</a
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
@@ -78,25 +78,26 @@
|
|||||||
<button id="settings-button">
|
<button id="settings-button">
|
||||||
<i class="fa-solid fa-gear"></i>
|
<i class="fa-solid fa-gear"></i>
|
||||||
</button>
|
</button>
|
||||||
|
<div id="connected-list-div">
|
||||||
|
<button id="show-connected-button" onclick="toggleClientList()">
|
||||||
|
Connected Blenders:
|
||||||
|
<span id="blender-count">0</span>
|
||||||
|
<i class="fas fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
<div id="client-list"></div>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div id="connected-list-div">
|
<div class="row-container">
|
||||||
<button id="show-connected-button" onclick="toggleClientList()">
|
<div id="ribbon-bar" class="ribbon-bar">
|
||||||
Connected Blenders:
|
<button
|
||||||
<span id="blender-count">0</span>
|
id="add-cost-schedule"
|
||||||
<i class="fas fa-chevron-down"></i>
|
class="action-button fas fa-plus"
|
||||||
</button>
|
data-tooltip="Add New Schedule"
|
||||||
<div id="client-list"></div>
|
></button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h1>Costing</h1>
|
|
||||||
<div id="" >
|
|
||||||
<li id="add-cost-schedule" class="action-button fas fa-plus"> </li>
|
|
||||||
Add New Schedule
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="cost-schedules" class="row-container"></div>
|
<div id="cost-schedules" class="row-container"></div>
|
||||||
|
|
||||||
<div id="cost-items" class="table-container"></div>
|
<div id="cost-items" class="table-container"></div>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p>BlenderBIM Version: {{version}}</p>
|
<p>BlenderBIM Version: {{version}}</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
Reference in New Issue
Block a user