mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-18 22:33:33 +00:00
1/ WEB UI Cost Schedule features to :
- delete cost items - duplicate cost items - delete individual cost values - update cost values on the fly - Shitf + LMB to recursively hide or show nested rows 2/ Improved UI for cost schedules 3/ Reuse css styles across pages with import statements 4/ Remove console/print statements
This commit is contained in:
@@ -41,49 +41,3 @@ th {
|
|||||||
/* zoom */
|
/* zoom */
|
||||||
transform: scale(0.9);
|
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;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
@import url("./shared.css");
|
||||||
|
@import url("./components/card.css");
|
||||||
|
@import url("./components/contextMenu.css");
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
/* prevent text from being selceted */
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
td:not(:first-child) {
|
||||||
|
text-align: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
|
||||||
|
border-right: 1px solid #ddd;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.nested {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-collapse {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.caret-cell {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid #e1e4e8;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color:inherit;
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
color:inherit;
|
||||||
|
transition: border-color 0.2s cubic-bezier(0.3, 0, 0.5, 1);
|
||||||
|
height: inherit;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus {
|
||||||
|
border-color: #0366d6;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 3px rgba(3, 102, 214, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#cost-items tr:hover, .highlighted {
|
||||||
|
background-color: #ffb936;
|
||||||
|
color : black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
background-color: #ffb936;
|
||||||
|
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;
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid black;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
z-index: 9999;
|
||||||
|
cursor: 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: #444444;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
background-color: #28a745; /* GitHub green color */
|
||||||
|
border: none;
|
||||||
|
color: #fff; /* White text color */
|
||||||
|
padding: 0.5em 1em; /* Adjust padding as needed */
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 6px; /* Slightly rounded corners */
|
||||||
|
font-weight: bold; /* Make the text bold */
|
||||||
|
text-decoration: none; /* Remove underline */
|
||||||
|
display: inline-block; /* Make the button inline */
|
||||||
|
transition: background-color 0.3s ease; /* Add a smooth transition */
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button:hover {
|
||||||
|
background-color: #218838; /* Darker green color on hover */
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
@import url("./components/card.css");
|
@import url("./components/card.css");
|
||||||
@import url("./components/contextMenu.css");
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--font-family: Arial, sans-serif;
|
--font-family: Arial, sans-serif;
|
||||||
|
|||||||
@@ -1,205 +1,4 @@
|
|||||||
:root {
|
@import url("./shared.css");
|
||||||
--font-family: Arial, sans-serif;
|
|
||||||
--base-font-size: 16px;
|
|
||||||
--margin-tiny: 0.125rem;
|
|
||||||
--margin-small: 0.625rem;
|
|
||||||
--margin-medium: 1.25rem;
|
|
||||||
--margin-large: 2.5rem;
|
|
||||||
--padding-tiny: 0.125rem;
|
|
||||||
--padding-small: 0.625rem;
|
|
||||||
--padding-medium: 1rem;
|
|
||||||
--font-size-large: 1.2rem;
|
|
||||||
--logo-height: 1.5rem;
|
|
||||||
--nav-height: 0.625rem;
|
|
||||||
--border-radius: 0.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root.dark {
|
|
||||||
color-scheme: dark;
|
|
||||||
--bg-color: #252525;
|
|
||||||
--text-color: #e0e0e0;
|
|
||||||
--nav-bg-color: #121212;
|
|
||||||
--nav-border-color: #25682a;
|
|
||||||
--nav-link-color: #fff;
|
|
||||||
--nav-link-hover-color: #3fb449;
|
|
||||||
--warning-color: #FFDB8F;
|
|
||||||
--border-color: #464444;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root.light {
|
|
||||||
color-scheme: light;
|
|
||||||
--bg-color: #ffffff;
|
|
||||||
--text-color: #000000;
|
|
||||||
--nav-bg-color: #f8f8f8;
|
|
||||||
--nav-border-color: #cccccc;
|
|
||||||
--nav-link-color: #000000;
|
|
||||||
--nav-link-hover-color: #38a63d;
|
|
||||||
--warning-color: #FF4500;
|
|
||||||
--border-color: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
*:not(.tabulator-cell) {
|
|
||||||
border-radius: var(--border-radius) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
font-size: var(--base-font-size);
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: var(--blender-window-background, var(--bg-color));
|
|
||||||
color: var(--blender-text, var(--text-color));
|
|
||||||
margin: 0;
|
|
||||||
font-family: var(--font-family);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
#container {
|
|
||||||
position: relative;
|
|
||||||
padding: var(--margin-small);
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
|
||||||
background-color: var(--blender-top-bar-header, var(--nav-bg-color));
|
|
||||||
height: var(--nav-height);
|
|
||||||
padding: var(--padding-medium) 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
border-bottom: 2px solid var(--blender-tab-outline, var(--nav-border-color));
|
|
||||||
}
|
|
||||||
|
|
||||||
nav .logo {
|
|
||||||
margin-left: var(--margin-large);
|
|
||||||
height: var(--logo-height);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav ul {
|
|
||||||
list-style-type: none;
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav ul li {
|
|
||||||
margin-right: var(--margin-large);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav ul li a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--blender-text, var(--nav-link-color));
|
|
||||||
}
|
|
||||||
|
|
||||||
nav ul li a:hover,
|
|
||||||
nav ul li a.active {
|
|
||||||
color: var(--blender-selected-object, var(--nav-link-hover-color));
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning,
|
|
||||||
.warning-icon {
|
|
||||||
color: var(--blender-info-warning, var(--warning-color));
|
|
||||||
}
|
|
||||||
|
|
||||||
#toggle-theme {
|
|
||||||
border: none;
|
|
||||||
background: none;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: var(--font-size-large);
|
|
||||||
margin-right: var(--margin-medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
#toggle-theme:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#client-list {
|
|
||||||
position: absolute;
|
|
||||||
top: calc(0 + var(--nav-height));
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
|
|
||||||
border: 1px solid var(--blender-tab-outline, var(--border-color));
|
|
||||||
background-color: var(--blender-panel-background, var(--nav-bg-color));
|
|
||||||
z-index: 1;
|
|
||||||
opacity: 0;
|
|
||||||
visibility: hidden;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#client-list.show {
|
|
||||||
opacity: 1;
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
#connected-list-div {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.client {
|
|
||||||
padding: var(--margin-small);
|
|
||||||
border-bottom: 1px solid var(--blender-tab-outline, var(--border-color));
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.client-details {
|
|
||||||
max-height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
padding-left: var(--padding-small);
|
|
||||||
transition: max-height 0.2s ease-out, padding 0.2s ease-out, opacity 0.1s ease-out;
|
|
||||||
background-color: var(--blender-panel-background, var(--nav-bg-color));
|
|
||||||
margin-top: var(--margin-tiny);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.client-details.show {
|
|
||||||
max-height: none;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.client-detail {
|
|
||||||
border-bottom: 1px solid var(--blender-tab-outline, var(--border-color));
|
|
||||||
padding: var(--padding-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
#show-connected-button {
|
|
||||||
width: auto;
|
|
||||||
background-color: var(--blender-panel-header, var(--bg-color));
|
|
||||||
font-size: var(--base-font-size);
|
|
||||||
color: var(--blender-text, var(--text-color))
|
|
||||||
}
|
|
||||||
|
|
||||||
#show-connected-button:hover,
|
|
||||||
.scroll-button:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll-button {
|
|
||||||
font-size: var(--base-font-size);
|
|
||||||
margin-left: var(--margin-tiny);
|
|
||||||
margin-top: var(--margin-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
background-color: var(--blender-top-bar-header, var(--nav-bg-color));
|
|
||||||
text-align: right;
|
|
||||||
padding: var(--padding-tiny);
|
|
||||||
border-top: 1px solid var(--blender-tab-outline, var(--nav-border-color));
|
|
||||||
}
|
|
||||||
|
|
||||||
footer p {
|
|
||||||
margin: 0;
|
|
||||||
color: var(--blender-text, var(--nav-link-color));
|
|
||||||
margin-right: var(--margin-small);
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-container {
|
.table-container {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -212,18 +11,6 @@ footer p {
|
|||||||
margin-top: var(--margin-small);
|
margin-top: var(--margin-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
button,
|
|
||||||
select {
|
|
||||||
background-color: var(--blender-button-background, var(--bg-color));
|
|
||||||
color: var(--blender-button-text, var(--text-color));
|
|
||||||
border-color: var(--blender-button-border, var(--border-color));
|
|
||||||
transition: filter 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
filter: brightness(1.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------ Overwriting Tabulator CSS rules ------------ */
|
/* ------------ Overwriting Tabulator CSS rules ------------ */
|
||||||
|
|
||||||
:root.light .tabulator-header,
|
:root.light .tabulator-header,
|
||||||
@@ -278,15 +65,12 @@ button:hover {
|
|||||||
color: var(--blender-text);
|
color: var(--blender-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
:root.blender .tabulator-selected .tabulator-cell,
|
:root.blender .tabulator-selected .tabulator-cell,
|
||||||
:root.blender .tabulator-row.tabulator-selected {
|
:root.blender .tabulator-row.tabulator-selected {
|
||||||
background-color: var(--blender-active-highlight) !important;
|
background-color: var(--blender-active-highlight) !important;
|
||||||
color: var(--blender-active-text-highlight) !important;
|
color: var(--blender-active-text-highlight) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
:root.blender .tabulator-selected:hover .tabulator-cell,
|
:root.blender .tabulator-selected:hover .tabulator-cell,
|
||||||
:root.blender .tabulator-row.tabulator-selected:hover {
|
:root.blender .tabulator-row.tabulator-selected:hover {
|
||||||
filter: brightness(1.2)
|
filter: brightness(1.2)
|
||||||
|
|||||||
@@ -0,0 +1,214 @@
|
|||||||
|
:root {
|
||||||
|
--font-family: Arial, sans-serif;
|
||||||
|
--base-font-size: 16px;
|
||||||
|
--margin-tiny: 0.125rem;
|
||||||
|
--margin-small: 0.625rem;
|
||||||
|
--margin-medium: 1.25rem;
|
||||||
|
--margin-large: 2.5rem;
|
||||||
|
--padding-tiny: 0.125rem;
|
||||||
|
--padding-small: 0.625rem;
|
||||||
|
--padding-medium: 1rem;
|
||||||
|
--font-size-large: 1.2rem;
|
||||||
|
--logo-height: 1.5rem;
|
||||||
|
--nav-height: 0.625rem;
|
||||||
|
--border-radius: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bg-color: #252525;
|
||||||
|
--text-color: #e0e0e0;
|
||||||
|
--nav-bg-color: #121212;
|
||||||
|
--nav-border-color: #25682a;
|
||||||
|
--nav-link-color: #fff;
|
||||||
|
--nav-link-hover-color: #3fb449;
|
||||||
|
--warning-color: #FFDB8F;
|
||||||
|
--border-color: #464444;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.light {
|
||||||
|
color-scheme: light;
|
||||||
|
--bg-color: #ffffff;
|
||||||
|
--text-color: #000000;
|
||||||
|
--nav-bg-color: #f8f8f8;
|
||||||
|
--nav-border-color: #cccccc;
|
||||||
|
--nav-link-color: #000000;
|
||||||
|
--nav-link-hover-color: #38a63d;
|
||||||
|
--warning-color: #FF4500;
|
||||||
|
--border-color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
*:not(.tabulator-cell) {
|
||||||
|
border-radius: var(--border-radius) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: var(--base-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--blender-window-background, var(--bg-color));
|
||||||
|
color: var(--blender-text, var(--text-color));
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--font-family);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
position: relative;
|
||||||
|
padding: var(--margin-small);
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
background-color: var(--blender-top-bar-header, var(--nav-bg-color));
|
||||||
|
height: var(--nav-height);
|
||||||
|
padding: var(--padding-medium) 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
border-bottom: 2px solid var(--blender-tab-outline, var(--nav-border-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
nav .logo {
|
||||||
|
margin-left: var(--margin-large);
|
||||||
|
height: var(--logo-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li {
|
||||||
|
margin-right: var(--margin-large);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--blender-text, var(--nav-link-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li a:hover,
|
||||||
|
nav ul li a.active {
|
||||||
|
color: var(--blender-selected-object, var(--nav-link-hover-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning,
|
||||||
|
.warning-icon {
|
||||||
|
color: var(--blender-info-warning, var(--warning-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggle-theme {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: var(--font-size-large);
|
||||||
|
margin-right: var(--margin-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggle-theme:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#client-list {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(0 + var(--nav-height));
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
|
||||||
|
border: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||||
|
background-color: var(--blender-panel-background, var(--nav-bg-color));
|
||||||
|
z-index: 1;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#client-list.show {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
#connected-list-div {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client {
|
||||||
|
padding: var(--margin-small);
|
||||||
|
border-bottom: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-details {
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: var(--padding-small);
|
||||||
|
transition: max-height 0.2s ease-out, padding 0.2s ease-out, opacity 0.1s ease-out;
|
||||||
|
background-color: var(--blender-panel-background, var(--nav-bg-color));
|
||||||
|
margin-top: var(--margin-tiny);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-details.show {
|
||||||
|
max-height: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-detail {
|
||||||
|
border-bottom: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||||
|
padding: var(--padding-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
#show-connected-button {
|
||||||
|
width: auto;
|
||||||
|
background-color: var(--blender-panel-header, var(--bg-color));
|
||||||
|
font-size: var(--base-font-size);
|
||||||
|
color: var(--blender-text, var(--text-color))
|
||||||
|
}
|
||||||
|
|
||||||
|
#show-connected-button:hover,
|
||||||
|
.scroll-button:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-button {
|
||||||
|
font-size: var(--base-font-size);
|
||||||
|
margin-left: var(--margin-tiny);
|
||||||
|
margin-top: var(--margin-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
background-color: var(--blender-top-bar-header, var(--nav-bg-color));
|
||||||
|
text-align: right;
|
||||||
|
padding: var(--padding-tiny);
|
||||||
|
border-top: 1px solid var(--blender-tab-outline, var(--nav-border-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
footer p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--blender-text, var(--nav-link-color));
|
||||||
|
margin-right: var(--margin-small);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
background-color: var(--blender-button-background, var(--bg-color));
|
||||||
|
color: var(--blender-button-text, var(--text-color));
|
||||||
|
border-color: var(--blender-button-border, var(--border-color));
|
||||||
|
transition: filter 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
filter: brightness(1.2);
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ function handleBlenderConnect(blenderId) {
|
|||||||
$("#blender-count").text(function (i, text) {
|
$("#blender-count").text(function (i, text) {
|
||||||
return parseInt(text, 10) + 1;
|
return parseInt(text, 10) + 1;
|
||||||
});
|
});
|
||||||
|
console.log("Blender connected", blenderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBlenderDisconnect(blenderId) {
|
function handleBlenderDisconnect(blenderId) {
|
||||||
@@ -53,15 +54,25 @@ function removeTableElement(blenderId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const costValueCallbacks = {
|
||||||
|
'editCostValues': editCostValues,
|
||||||
|
'addCostValue': addCostValue,
|
||||||
|
'deleteCostValue': deleteCostValue,
|
||||||
|
};
|
||||||
|
|
||||||
function handleCostValueData(data) {
|
function handleCostValueData(data) {
|
||||||
|
|
||||||
const costItemId = data.data["cost_value"]["cost_item_id"];
|
const costItemId = data.data["cost_value"]["cost_item_id"];
|
||||||
const costValueId = data.data["cost_value"]["cost_value_id"];
|
const costValueId = data.data["cost_value"]["cost_value_id"];
|
||||||
console.log("Handling cost value data", costItemId, costValueId);
|
console.log("Handling cost value data", costItemId, costValueId);
|
||||||
CostUI.addNewCostValueRow(costItemId, costValueId);
|
CostUI.addNewCostValueRow(costItemId, costValueId, costValueCallbacks);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deleteCostValue(costItemId, costValueId) {
|
||||||
|
executeOperator({ type: "deleteCostValue", costItemId: costItemId, costValueId: costValueId });
|
||||||
|
}
|
||||||
|
|
||||||
function handleConnectedClients(data) {
|
function handleConnectedClients(data) {
|
||||||
$("#blender-count").text(data.length);
|
$("#blender-count").text(data.length);
|
||||||
|
|
||||||
@@ -77,8 +88,12 @@ function handleCostValuesData(data) {
|
|||||||
callbacks: {
|
callbacks: {
|
||||||
'editCostValues': editCostValues,
|
'editCostValues': editCostValues,
|
||||||
'addCostValue': addCostValue,
|
'addCostValue': addCostValue,
|
||||||
|
'deleteCostValue': deleteCostValue,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
CostUI.highlightCostItem(data.data["cost_values"]["cost_item_id"]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addCostValue(costItemId) {
|
function addCostValue(costItemId) {
|
||||||
@@ -93,6 +108,10 @@ function editCostValues(costItemId, costValues) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deleteCostItem(costItemId) {
|
||||||
|
executeOperator({ type: "deleteCostItem", costItemId: costItemId });
|
||||||
|
}
|
||||||
|
|
||||||
function handleThemeData(themeData) {
|
function handleThemeData(themeData) {
|
||||||
function arrayToRgbString(arr) {
|
function arrayToRgbString(arr) {
|
||||||
const [r, g, b, a] = arr.map((num) => Math.round(num * 255));
|
const [r, g, b, a] = arr.map((num) => Math.round(num * 255));
|
||||||
@@ -165,14 +184,23 @@ function handleCostSchedulesData(data) {
|
|||||||
const card = CostUI.createCard(costSchedule.Name, mainContainer, callback);
|
const card = CostUI.createCard(costSchedule.Name, mainContainer, callback);
|
||||||
costScheduleDiv.append(card);
|
costScheduleDiv.append(card);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const firstCostSchedule = costSchedules[0];
|
||||||
|
if (firstCostSchedule) {
|
||||||
|
loadCostSchedule(firstCostSchedule.id, blenderId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCostItemsData(data) {
|
function handleCostItemsData(data) {
|
||||||
|
console.log("Handling cost items data", data);
|
||||||
CostUI.createCostSchedule({
|
CostUI.createCostSchedule({
|
||||||
data: data.data["cost_items"],
|
data: data.data["cost_items"],
|
||||||
blenderID: data.blenderId,
|
blenderID: data.blenderId,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
"addCostItem": addCostItem,
|
"addCostItem": addCostItem,
|
||||||
|
"deleteCostItem": deleteCostItem,
|
||||||
|
"duplicateCostItem": duplicateCostItem,
|
||||||
"selectAssignedElements": selectAssignedElements,
|
"selectAssignedElements": selectAssignedElements,
|
||||||
'editCostItemName': editCostItemName,
|
'editCostItemName': editCostItemName,
|
||||||
'enableEditingCostValues': enableEditingCostValues,
|
'enableEditingCostValues': enableEditingCostValues,
|
||||||
@@ -180,6 +208,10 @@ function handleCostItemsData(data) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function duplicateCostItem(costItemId) {
|
||||||
|
executeOperator({ type: "duplicateCostItem", costItemId: costItemId });
|
||||||
|
}
|
||||||
|
|
||||||
function executeOperator(operator, blenderId) {
|
function executeOperator(operator, blenderId) {
|
||||||
const msg = {
|
const msg = {
|
||||||
sourcePage: "cost",
|
sourcePage: "cost",
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ function connectSocket() {
|
|||||||
socket.on("blender_disconnect", handleBlenderDisconnect);
|
socket.on("blender_disconnect", handleBlenderDisconnect);
|
||||||
socket.on("connect", handleWebConnect);
|
socket.on("connect", handleWebConnect);
|
||||||
socket.on("connected_clients", handleConnectedClients);
|
socket.on("connected_clients", handleConnectedClients);
|
||||||
socket.on("default_data", handleDefaultData);
|
//socket.on("default_data", handleDefaultData);
|
||||||
socket.on("theme_data", handleThemeData);
|
socket.on("theme_data", handleThemeData);
|
||||||
socket.on("drawings_data", handleDrawingsData);
|
socket.on("drawings_data", handleDrawingsData);
|
||||||
socket.on("svg_data", handleSvgData);
|
socket.on("svg_data", handleSvgData);
|
||||||
@@ -37,6 +37,7 @@ function connectSocket() {
|
|||||||
|
|
||||||
// function used to get drawings data from Bonsai
|
// function used to get drawings data from Bonsai
|
||||||
function handleWebConnect() {
|
function handleWebConnect() {
|
||||||
|
console.log("Handle Web Connect")
|
||||||
getDrawingsData();
|
getDrawingsData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +74,6 @@ function handleDefaultData(data) {
|
|||||||
|
|
||||||
// Function to handle 'blender_disconnect' event
|
// Function to handle 'blender_disconnect' event
|
||||||
function handleBlenderDisconnect(blenderId) {
|
function handleBlenderDisconnect(blenderId) {
|
||||||
console.log("blender disconnected: ", blenderId);
|
|
||||||
if (connectedClients.hasOwnProperty(blenderId)) {
|
if (connectedClients.hasOwnProperty(blenderId)) {
|
||||||
delete connectedClients[blenderId];
|
delete connectedClients[blenderId];
|
||||||
removeSelectOption(blenderId);
|
removeSelectOption(blenderId);
|
||||||
@@ -97,8 +97,6 @@ function handleConnectedClients(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleThemeData(themeData) {
|
function handleThemeData(themeData) {
|
||||||
// console.log(themeData);
|
|
||||||
|
|
||||||
function arrayToRgbString(arr) {
|
function arrayToRgbString(arr) {
|
||||||
const [r, g, b, a] = arr.map((num) => Math.round(num * 255));
|
const [r, g, b, a] = arr.map((num) => Math.round(num * 255));
|
||||||
if (a !== undefined) {
|
if (a !== undefined) {
|
||||||
@@ -129,6 +127,7 @@ function handleThemeData(themeData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleDrawingsData(data) {
|
function handleDrawingsData(data) {
|
||||||
|
console.log("Running handleDrawingsData");
|
||||||
const blenderId = data["blenderId"];
|
const blenderId = data["blenderId"];
|
||||||
|
|
||||||
console.log(data);
|
console.log(data);
|
||||||
|
|||||||
@@ -23,11 +23,10 @@ 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"; // Required for the resizer handle
|
th.style.position = "relative";
|
||||||
tr.appendChild(th);
|
tr.appendChild(th);
|
||||||
|
|
||||||
// Add resizer handle
|
if (i < columnHeaders.length - 1) {
|
||||||
if (i < columnHeaders.length - 1) { // No resizer for the last column
|
|
||||||
const resizer = document.createElement("div");
|
const resizer = document.createElement("div");
|
||||||
resizer.classList.add("resizer");
|
resizer.classList.add("resizer");
|
||||||
th.appendChild(resizer);
|
th.appendChild(resizer);
|
||||||
@@ -39,12 +38,8 @@ export class CostUI {
|
|||||||
table.appendChild(tbody);
|
table.appendChild(tbody);
|
||||||
document.getElementById("cost-items").appendChild(table);
|
document.getElementById("cost-items").appendChild(table);
|
||||||
|
|
||||||
// Add CSS to set column widths, resizer styles, hover effect, and color scheme
|
|
||||||
CostUI.addTableStyles(id);
|
CostUI.addTableStyles(id);
|
||||||
|
|
||||||
// Create context menu
|
|
||||||
CostUI.createContextMenu(callbacks);
|
CostUI.createContextMenu(callbacks);
|
||||||
|
|
||||||
table.get_blender_id = function() {
|
table.get_blender_id = function() {
|
||||||
return this.getAttribute("id").split("-")[2];
|
return this.getAttribute("id").split("-")[2];
|
||||||
};
|
};
|
||||||
@@ -69,19 +64,17 @@ export class CostUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static createContextMenu(callbacks) {
|
static createContextMenu(callbacks) {
|
||||||
// Create context menu
|
|
||||||
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");
|
||||||
contextMenu.innerHTML = `
|
contextMenu.innerHTML = `
|
||||||
|
<button id="edit-cost-values-button">Edit Cost Values</button>
|
||||||
<button id="add-cost-item-button">Add sub-cost</button>
|
<button id="add-cost-item-button">Add sub-cost</button>
|
||||||
<button id="edit-cost-values-button">Edit</button>
|
|
||||||
<button id="delete-button">Delete</button>
|
|
||||||
<button id="duplicate-button">Duplicate</button>
|
<button id="duplicate-button">Duplicate</button>
|
||||||
|
<button id="delete-button">Delete</button>
|
||||||
`;
|
`;
|
||||||
document.body.appendChild(contextMenu);
|
document.body.appendChild(contextMenu);
|
||||||
|
|
||||||
// Add event listeners for context menu
|
|
||||||
document.addEventListener("contextmenu", function(event) {
|
document.addEventListener("contextmenu", function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const targetRow = event.target.closest("tr");
|
const targetRow = event.target.closest("tr");
|
||||||
@@ -92,7 +85,6 @@ export class CostUI {
|
|||||||
contextMenu.style.left = `${event.pageX}px`;
|
contextMenu.style.left = `${event.pageX}px`;
|
||||||
contextMenu.style.top = `${event.pageY}px`;
|
contextMenu.style.top = `${event.pageY}px`;
|
||||||
|
|
||||||
// Store the target row in the context menu for later use
|
|
||||||
contextMenu.targetRow = targetRow;
|
contextMenu.targetRow = targetRow;
|
||||||
contextMenu.targetCell = targetCell;
|
contextMenu.targetCell = targetCell;
|
||||||
} else {
|
} else {
|
||||||
@@ -109,7 +101,6 @@ export class CostUI {
|
|||||||
|
|
||||||
const editCostValuesButton = document.getElementById("edit-cost-values-button");
|
const editCostValuesButton = document.getElementById("edit-cost-values-button");
|
||||||
editCostValuesButton.addEventListener("click", function() {
|
editCostValuesButton.addEventListener("click", function() {
|
||||||
console.log("Edit cost values executed");
|
|
||||||
const targetRow = document.getElementById("context-menu").targetRow;
|
const targetRow = document.getElementById("context-menu").targetRow;
|
||||||
const targetCell = document.getElementById("context-menu").targetCell;
|
const targetCell = document.getElementById("context-menu").targetCell;
|
||||||
if (targetRow) {
|
if (targetRow) {
|
||||||
@@ -133,24 +124,45 @@ export class CostUI {
|
|||||||
addButton.dataset.listenerAdded = "true";
|
addButton.dataset.listenerAdded = "true";
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById("delete-button").addEventListener("click", function() {
|
const deleteButton = document.getElementById("delete-button");
|
||||||
const targetRow = document.getElementById("context-menu").targetRow;
|
if (deleteButton && !deleteButton.hasListener) {
|
||||||
if (targetRow) {
|
deleteButton.addEventListener("click", function() {
|
||||||
// Implement your delete action here
|
const targetRow = document.getElementById("context-menu").targetRow;
|
||||||
console.log("Delete row:", targetRow.getAttribute("id"));
|
if (targetRow) {
|
||||||
targetRow.remove();
|
const costItemId = parseInt(targetRow.getAttribute("id"));
|
||||||
}
|
let expandedState = JSON.parse(localStorage.getItem('expandedState')) || {};
|
||||||
});
|
expandedState = CostUI.deleteRow(targetRow, expandedState)
|
||||||
|
localStorage.setItem('expandedState', JSON.stringify(expandedState));
|
||||||
|
callbacks.deleteCostItem(costItemId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
deleteButton.hasListener = true;
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById("duplicate-button").addEventListener("click", function() {
|
const duplicateButton = document.getElementById("duplicate-button");
|
||||||
const targetRow = document.getElementById("context-menu").targetRow;
|
if (duplicateButton && !duplicateButton.hasListener) {
|
||||||
if (targetRow) {
|
duplicateButton.addEventListener("click", function() {
|
||||||
// Implement your duplicate action here
|
const targetRow = document.getElementById("context-menu").targetRow;
|
||||||
console.log("Duplicate row:", targetRow.getAttribute("id"));
|
if (targetRow) {
|
||||||
const newRow = targetRow.cloneNode(true);
|
const costItemId = parseInt(targetRow.getAttribute("id"));
|
||||||
targetRow.parentElement.appendChild(newRow);
|
callbacks.duplicateCostItem(costItemId);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
duplicateButton.hasListener = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static deleteRow(targetRow, expandedState) {
|
||||||
|
if (!targetRow) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete expandedState[targetRow.id]
|
||||||
|
targetRow.remove()
|
||||||
|
const subRows = document.querySelectorAll(`[parent-id='${targetRow.id}']`);
|
||||||
|
subRows.forEach(subRow => {
|
||||||
|
CostUI.deleteRow(subRow, expandedState)
|
||||||
});
|
});
|
||||||
|
return expandedState
|
||||||
}
|
}
|
||||||
|
|
||||||
static addResizer(resizer) {
|
static addResizer(resizer) {
|
||||||
@@ -176,18 +188,15 @@ export class CostUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static generateColorScheme(baseColor) {
|
static generateColorScheme(baseColor) {
|
||||||
// This function generates a color scheme based on the base color
|
const levels = 10;
|
||||||
// For simplicity, we'll just lighten the base color for each level
|
|
||||||
const levels = 7; // Number of levels of nesting
|
|
||||||
const colorScheme = [];
|
const colorScheme = [];
|
||||||
for (let i = 0; i < levels; i++) {
|
for (let i = 0; i < levels; i++) {
|
||||||
colorScheme.push(CostUI.lightenColor(baseColor, i * 7));
|
colorScheme.push(CostUI.lightenColor(baseColor, i * 5));
|
||||||
}
|
}
|
||||||
return colorScheme;
|
return colorScheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
static lightenColor(color, percent) {
|
static lightenColor(color, percent) {
|
||||||
// This function lightens a color by a given percentage
|
|
||||||
const num = parseInt(color.slice(1), 16),
|
const num = parseInt(color.slice(1), 16),
|
||||||
amt = Math.round(2.55 * percent),
|
amt = Math.round(2.55 * percent),
|
||||||
R = (num >> 16) + amt,
|
R = (num >> 16) + amt,
|
||||||
@@ -199,7 +208,7 @@ export class CostUI {
|
|||||||
static applyColorScheme(tableId, colorScheme) {
|
static applyColorScheme(tableId, colorScheme) {
|
||||||
const rows = document.querySelectorAll(`#${tableId} tbody tr`);
|
const rows = document.querySelectorAll(`#${tableId} tbody tr`);
|
||||||
rows.forEach((row, index) => {
|
rows.forEach((row, index) => {
|
||||||
const level = index % colorScheme.length; // Assuming level is determined by index for simplicity
|
const level = index % colorScheme.length;
|
||||||
row.style.backgroundColor = colorScheme[level];
|
row.style.backgroundColor = colorScheme[level];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -209,7 +218,7 @@ export class CostUI {
|
|||||||
const colorPicker = document.createElement("input");
|
const colorPicker = document.createElement("input");
|
||||||
colorPicker.type = "color";
|
colorPicker.type = "color";
|
||||||
colorPicker.id = "color-picker";
|
colorPicker.id = "color-picker";
|
||||||
colorPicker.value = "#ff0000"; // Default color
|
colorPicker.value = "#ff0000";
|
||||||
const colorText = document.createElement("p");
|
const colorText = document.createElement("p");
|
||||||
colorText.textContent = "Select a color to change row color:";
|
colorText.textContent = "Select a color to change row color:";
|
||||||
document.getElementById("UI").appendChild(colorText);
|
document.getElementById("UI").appendChild(colorText);
|
||||||
@@ -231,25 +240,26 @@ export class CostUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static createCostItem(data, container, nestingLevel = 0, parentID = null, callbacks = {}) {
|
static createCostItem(data, container, nestingLevel = 0, parentID = null, callbacks = {}) {
|
||||||
data.forEach(obj => {
|
data.forEach(costItem => {
|
||||||
const row = CostUI.createRow(obj, nestingLevel, parentID, callbacks);
|
const row = CostUI.createRow(costItem, nestingLevel, parentID, callbacks);
|
||||||
container.appendChild(row);
|
container.appendChild(row);
|
||||||
|
|
||||||
if (obj.is_nested_by && obj.is_nested_by.length > 0) {
|
if (costItem.is_nested_by && costItem.is_nested_by.length > 0) {
|
||||||
CostUI.createCostItem(obj.is_nested_by, container, nestingLevel + 1, obj.id, callbacks);
|
CostUI.createCostItem(costItem.is_nested_by, container, nestingLevel + 1, costItem.id, callbacks);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static createRow(obj, nestingLevel, parentID, callbacks = {}) {
|
static createRow(costItem, nestingLevel, parentID, callbacks = {}) {
|
||||||
const row = CostUI.createTableRow(obj, nestingLevel, parentID);
|
const totalQuantity = costItem.TotalCostQuantity ? parseFloat(costItem.TotalCostQuantity).toFixed(2) : '-';
|
||||||
const expandButton = CostUI.createExpandButton(obj);
|
const row = CostUI.createTableRow(costItem, nestingLevel, parentID);
|
||||||
const nameCell = CostUI.createNameCell(obj, nestingLevel, expandButton, callbacks);
|
const expandButton = CostUI.createExpandButton(costItem);
|
||||||
const totalCostQuantityCell = CostUI.createTableCell(obj.TotalCostQuantity);
|
const nameCell = CostUI.createNameCell(costItem, nestingLevel, expandButton, callbacks);
|
||||||
const unitSymbolCell = CostUI.createTableCell(obj.UnitSymbol);
|
const totalCostQuantityCell = CostUI.createTableCell(totalQuantity);
|
||||||
const totalAppliedValueCell = CostUI.createTableCell(obj.TotalAppliedValue);
|
const unitSymbolCell = CostUI.createTableCell(costItem.UnitSymbol);
|
||||||
const totalCostCell = CostUI.createTotalCostCell(obj);
|
const totalAppliedValueCell = CostUI.createTableCell(costItem.TotalAppliedValue);
|
||||||
const flexContainerCell = CostUI.createFlexContainerCell(obj, callbacks);
|
const totalCostCell = CostUI.createTotalCostCell(costItem);
|
||||||
|
const flexContainerCell = CostUI.createFlexContainerCell(costItem, callbacks);
|
||||||
|
|
||||||
row.appendChild(nameCell);
|
row.appendChild(nameCell);
|
||||||
row.appendChild(totalCostQuantityCell);
|
row.appendChild(totalCostQuantityCell);
|
||||||
@@ -270,49 +280,167 @@ export class CostUI {
|
|||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createTableRow(obj, nestingLevel, parentID) {
|
static createTableRow(costItem, nestingLevel, parentID) {
|
||||||
const row = document.createElement("tr");
|
const row = document.createElement("tr");
|
||||||
row.setAttribute("id", obj.id);
|
row.setAttribute("id", costItem.id);
|
||||||
row.setAttribute("parent-id", parentID);
|
row.setAttribute("parent-id", parentID);
|
||||||
if (nestingLevel > 0) {
|
if (nestingLevel > 0) {
|
||||||
row.classList.add("nested");
|
|
||||||
row.classList.add(`level-${nestingLevel}`);
|
row.classList.add(`level-${nestingLevel}`);
|
||||||
}
|
}
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createExpandButton(obj) {
|
static createExpandButton(row) {
|
||||||
const expandButton = document.createElement("button");
|
const expandButton = document.createElement("i");
|
||||||
expandButton.classList.add("toggle-button");
|
expandButton.classList.add("action-button");
|
||||||
if (obj.is_nested_by && obj.is_nested_by.length > 0) {
|
expandButton.classList.add("fa-solid");
|
||||||
expandButton.textContent = ">";
|
expandButton.classList.add("fa-angle-right");
|
||||||
|
|
||||||
|
if (row.is_nested_by && row.is_nested_by.length > 0) {
|
||||||
|
expandButton.classList.add('fa-angle-right');
|
||||||
} else {
|
} else {
|
||||||
expandButton.style.visibility = "hidden";
|
expandButton.style.visibility = "hidden";
|
||||||
}
|
}
|
||||||
expandButton.addEventListener("click", function() {
|
|
||||||
CostUI.contractExpandRow.call(this, obj.id);
|
expandButton.addEventListener("click", function(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
let isExpanded = CostUI.isRowExpanded(row.id);
|
||||||
|
console.log("Row is expanded", isExpanded);
|
||||||
|
if (event.shiftKey) {
|
||||||
|
if (isExpanded) {
|
||||||
|
CostUI.contractRow(row.id);
|
||||||
|
CostUI.updateExpandedState(row.id, false, true);
|
||||||
|
} else {
|
||||||
|
CostUI.expandRow(row.id);
|
||||||
|
CostUI.updateExpandedState(row.id, true, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isExpanded) {
|
||||||
|
CostUI.contractRow(row.id);
|
||||||
|
CostUI.updateExpandedState(row.id, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CostUI.expandRow(row.id);
|
||||||
|
CostUI.updateExpandedState(row.id, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CostUI.applyExpandedState();
|
||||||
});
|
});
|
||||||
|
|
||||||
return expandButton;
|
return expandButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createNameCell(obj, nestingLevel, expandButton, callbacks) {
|
|
||||||
|
static hideRow(id) {
|
||||||
|
const row = document.getElementById(id);
|
||||||
|
if (row) {
|
||||||
|
row.classList.add("nested");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static contractRow(parentId, recursive=false) {
|
||||||
|
CostUI.updateToggleButton(parentId, 'contract');
|
||||||
|
const rows = document.querySelectorAll(`[parent-id='${parentId}']`);
|
||||||
|
rows.forEach(row => {
|
||||||
|
const childId = row.getAttribute('id');
|
||||||
|
CostUI.hideRow(childId);
|
||||||
|
if (recursive || (childId && CostUI.isRowExpanded(childId))) {
|
||||||
|
CostUI.contractRow(childId, recursive)
|
||||||
|
}
|
||||||
|
// CostUI.contractRow(childId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static showRow(id) {
|
||||||
|
const row = document.getElementById(id);
|
||||||
|
if (row) {
|
||||||
|
row.classList.remove("nested");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static updateToggleButton(id, type='contract') {
|
||||||
|
const toggleButton = document.querySelector(`[id='${id}'] .action-button`);
|
||||||
|
if (!toggleButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type === 'expand') {
|
||||||
|
toggleButton.classList.remove("fa-angle-right");
|
||||||
|
toggleButton.classList.add("fa-angle-up");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toggleButton.classList.remove("fa-angle-up");
|
||||||
|
toggleButton.classList.add("fa-angle-right");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static expandRow(parentId, recursive=false) {
|
||||||
|
CostUI.updateExpandedState(parentId, true);
|
||||||
|
CostUI.updateToggleButton(parentId, 'expand');
|
||||||
|
const rows = document.querySelectorAll(`[parent-id='${parentId}']`);
|
||||||
|
rows.forEach(row => {
|
||||||
|
const childId = row.getAttribute('id');
|
||||||
|
CostUI.showRow(childId);
|
||||||
|
if (recursive || (childId && CostUI.isRowExpanded(childId))) {
|
||||||
|
CostUI.expandRow(childId, recursive);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static updateExpandedState(id, isExpanded, recursive=false) {
|
||||||
|
const expandedState = JSON.parse(localStorage.getItem('expandedState')) || {};
|
||||||
|
expandedState[id] = isExpanded;
|
||||||
|
localStorage.setItem('expandedState', JSON.stringify(expandedState));
|
||||||
|
if (recursive) {
|
||||||
|
const rows = document.querySelectorAll(`[parent-id='${id}']`);
|
||||||
|
rows.forEach(row => {
|
||||||
|
CostUI.updateExpandedState(row.id, isExpanded, recursive);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static applyExpandedState() {
|
||||||
|
const expandedState = JSON.parse(localStorage.getItem('expandedState')) || CostUI.getAllCostItemsIds().reduce((acc, id) => {
|
||||||
|
acc[id] = true;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
console.log("Expanded state", expandedState);
|
||||||
|
|
||||||
|
Object.keys(expandedState).forEach(id => {
|
||||||
|
if (expandedState[id]) {
|
||||||
|
CostUI.expandRow(id);
|
||||||
|
} else {
|
||||||
|
CostUI.contractRow(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static isRowExpanded(id) {
|
||||||
|
const expandedState = JSON.parse(localStorage.getItem('expandedState')) || {};
|
||||||
|
return expandedState[id] || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static createNameCell(costItem, nestingLevel, expandButton, callbacks) {
|
||||||
const nameCell = document.createElement("td");
|
const nameCell = document.createElement("td");
|
||||||
const nameInput = document.createElement("input");
|
const nameInput = document.createElement("input");
|
||||||
nameInput.value = obj.name ? obj.name : "Unnamed";
|
nameInput.value = costItem.name ? costItem.name : "Unnamed";
|
||||||
|
|
||||||
nameInput.addEventListener("change", function() {
|
nameInput.addEventListener("change", function() {
|
||||||
callbacks.editCostItemName ? callbacks.editCostItemName(obj.id, this.value) : null;
|
callbacks.editCostItemName ? callbacks.editCostItemName(costItem.id, this.value) : null;
|
||||||
});
|
});
|
||||||
|
|
||||||
nameInput.addEventListener("keydown", function(event) {
|
nameInput.addEventListener("keydown", function(event) {
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
callbacks.editCostItemName ? callbacks.editCostItemName(obj.id, this.value) : null;
|
callbacks.editCostItemName ? callbacks.editCostItemName(costItem.id, this.value) : null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
nameCell.style.paddingLeft = nestingLevel * 20 + "px";
|
nameCell.style.paddingLeft = nestingLevel * 20 + "px";
|
||||||
nameCell.appendChild(expandButton);
|
nameCell.appendChild(expandButton);
|
||||||
nameCell.appendChild(nameInput);
|
nameCell.appendChild(nameInput);
|
||||||
|
const widthButton = 20;
|
||||||
|
nameInput.style.width = "calc(100% - " + widthButton + "%)";
|
||||||
return nameCell;
|
return nameCell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,19 +450,19 @@ export class CostUI {
|
|||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createTotalCostCell(obj) {
|
static createTotalCostCell(costItem) {
|
||||||
const totalCostCell = document.createElement("td");
|
const totalCostCell = document.createElement("td");
|
||||||
const totalCost = parseFloat(obj.TotalCost).toFixed(2);
|
const totalCost = parseFloat(costItem.TotalCost).toFixed(2);
|
||||||
totalCostCell.textContent = obj.is_sum ? totalCost + " (Σ)" : totalCost;
|
totalCostCell.textContent = costItem.is_sum ? totalCost + " (Σ)" : totalCost;
|
||||||
return totalCostCell;
|
return totalCostCell;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createFlexContainerCell(obj, callbacks) {
|
static createFlexContainerCell(costItem, callbacks) {
|
||||||
const divFlex = document.createElement("div");
|
const divFlex = document.createElement("div");
|
||||||
divFlex.classList.add("flex-container");
|
divFlex.classList.add("flex-container");
|
||||||
|
|
||||||
const addButton = CostUI.createAddButton(obj, callbacks);
|
const addButton = CostUI.createAddButton(costItem, callbacks);
|
||||||
const selectButton = CostUI.createSelectButton(obj, callbacks);
|
const selectButton = CostUI.createSelectButton(costItem, callbacks);
|
||||||
|
|
||||||
divFlex.appendChild(addButton);
|
divFlex.appendChild(addButton);
|
||||||
divFlex.appendChild(selectButton);
|
divFlex.appendChild(selectButton);
|
||||||
@@ -344,102 +472,52 @@ export class CostUI {
|
|||||||
return flexContainerCell;
|
return flexContainerCell;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createAddButton(obj, callbacks) {
|
static createAddButton(costItem, callbacks) {
|
||||||
const addButton = document.createElement("button");
|
const addButton = document.createElement("button");
|
||||||
addButton.textContent = "+";
|
addButton.textContent = "+";
|
||||||
addButton.classList.add("add-button");
|
addButton.classList.add("add-button");
|
||||||
addButton.addEventListener("click", function(e) {
|
addButton.addEventListener("click", function(e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
callbacks.addCostItem ? callbacks.addCostItem(obj.id) : null;
|
callbacks.addCostItem ? callbacks.addCostItem(costItem.id) : null;
|
||||||
});
|
});
|
||||||
return addButton;
|
return addButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createSelectButton(obj, callbacks) {
|
static createSelectButton(costItem, callbacks) {
|
||||||
const selectButton = document.createElement("button");
|
const selectButton = document.createElement("button");
|
||||||
|
selectButton.classList.add("action-button");
|
||||||
selectButton.textContent = "Select";
|
selectButton.textContent = "Select";
|
||||||
selectButton.addEventListener("click", function(e) {
|
selectButton.addEventListener("click", function(e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
callbacks.selectAssignedElements ? callbacks.selectAssignedElements(obj.id) : null;
|
callbacks.selectAssignedElements ? callbacks.selectAssignedElements(costItem.id) : null;
|
||||||
});
|
});
|
||||||
return selectButton;
|
return selectButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
static hideNestedRows(parentId) {
|
static highlightCostItem(id) {
|
||||||
const rows = document.querySelectorAll(`[parent-id='${parentId}']`);
|
const row = document.getElementById(id);
|
||||||
rows.forEach(row => {
|
if (row) {
|
||||||
row.classList.add("nested");
|
row.classList.add("highlighted");
|
||||||
const childId = row.getAttribute('id');
|
|
||||||
if (childId) {
|
|
||||||
CostUI.hideNestedRows(childId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static showNestedRows(parentId) {
|
|
||||||
const rows = document.querySelectorAll(`[parent-id='${parentId}']`);
|
|
||||||
rows.forEach(row => {
|
|
||||||
row.classList.remove("nested");
|
|
||||||
const childId = row.getAttribute('id');
|
|
||||||
if (childId && CostUI.isRowExpanded(childId)) {
|
|
||||||
CostUI.showNestedRows(childId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static contractExpandRow(id) {
|
|
||||||
const rows = document.querySelectorAll(`[parent-id='${id}']`);
|
|
||||||
if (rows.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let isVisible = false;
|
|
||||||
rows.forEach(row => {
|
|
||||||
if (!row.classList.contains("nested")) {
|
|
||||||
isVisible = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isVisible) {
|
|
||||||
CostUI.hideNestedRows(id);
|
|
||||||
this.textContent = ">";
|
|
||||||
CostUI.updateExpandedState(id, false);
|
|
||||||
} else {
|
|
||||||
CostUI.showNestedRows(id);
|
|
||||||
this.textContent = "^";
|
|
||||||
CostUI.updateExpandedState(id, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateExpandedState(id, isExpanded) {
|
static unhighlightCostItem(id) {
|
||||||
const expandedState = JSON.parse(localStorage.getItem('expandedState')) || {};
|
const row = document.getElementById(id);
|
||||||
expandedState[id] = isExpanded;
|
if (row) {
|
||||||
localStorage.setItem('expandedState', JSON.stringify(expandedState));
|
row.classList.remove("highlighted");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static applyExpandedState() {
|
static getAllCostItemsIds() {
|
||||||
const expandedState = JSON.parse(localStorage.getItem('expandedState')) || {};
|
const rows = document.querySelectorAll("#cost-items tr");
|
||||||
Object.keys(expandedState).forEach(id => {
|
const ids = [];
|
||||||
if (expandedState[id]) {
|
rows.forEach(row => {
|
||||||
CostUI.showNestedRows(id);
|
ids.push(row.id);
|
||||||
const toggleButton = document.querySelector(`[id='${id}'] .toggle-button`);
|
|
||||||
if (toggleButton) {
|
|
||||||
toggleButton.textContent = "^";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
CostUI.hideNestedRows(id);
|
|
||||||
const toggleButton = document.querySelector(`[id='${id}'] .toggle-button`);
|
|
||||||
if (toggleButton) {
|
|
||||||
toggleButton.textContent = ">";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
static isRowExpanded(id) {
|
|
||||||
const expandedState = JSON.parse(localStorage.getItem('expandedState')) || {};
|
|
||||||
return expandedState[id] || false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static text(label) {
|
static text(label) {
|
||||||
const text = document.createElement("p");
|
const text = document.createElement("p");
|
||||||
@@ -459,7 +537,7 @@ export class CostUI {
|
|||||||
cardTitle.textContent = title;
|
cardTitle.textContent = title;
|
||||||
|
|
||||||
const cardButton = document.createElement("button");
|
const cardButton = document.createElement("button");
|
||||||
cardButton.classList.add("btn", "btn-primary");
|
cardButton.classList.add("action-button");
|
||||||
cardButton.textContent = "Load";
|
cardButton.textContent = "Load";
|
||||||
cardButton.addEventListener("click", callback);
|
cardButton.addEventListener("click", callback);
|
||||||
|
|
||||||
@@ -471,8 +549,15 @@ export class CostUI {
|
|||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getRowNameCell(costItemId) {
|
||||||
|
return document.getElementById(costItemId).querySelector("td input");
|
||||||
|
}
|
||||||
|
|
||||||
|
static getCostItemName(costItemId) {
|
||||||
|
return CostUI.getRowNameCell(costItemId).value;
|
||||||
|
}
|
||||||
|
|
||||||
static createCostValuesForm({ costItemId, costValues, callbacks }) {
|
static createCostValuesForm({ costItemId, costValues, callbacks }) {
|
||||||
// Check if the form already exists and remove it if it does
|
|
||||||
const formId = "cost-values-form-" + costItemId;
|
const formId = "cost-values-form-" + costItemId;
|
||||||
let existingForm = document.getElementById(formId);
|
let existingForm = document.getElementById(formId);
|
||||||
if (existingForm) {
|
if (existingForm) {
|
||||||
@@ -480,14 +565,14 @@ export class CostUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const form = CostUI.createFormElement(formId, "cost-values-form");
|
const form = CostUI.createFormElement(formId, "cost-values-form");
|
||||||
|
const costItemName = CostUI.getCostItemName(costItemId);
|
||||||
const header = CostUI.createHeader("Cost Values Form");
|
const header = CostUI.createHeader("Cost Values Form for " + costItemName);
|
||||||
form.appendChild(header);
|
form.appendChild(header);
|
||||||
|
|
||||||
const table = CostUI.createCostValuesTable(costItemId, ["Type", "Category", "Value"]);
|
const table = CostUI.createCostValuesTable(costItemId, ["Type", "Category", "Value", ""]);
|
||||||
|
|
||||||
costValues.forEach(costValue => {
|
costValues.forEach(costValue => {
|
||||||
const tr = CostUI.createCostvaluesRow(costItemId, costValue);
|
const tr = CostUI.createCostvaluesRow(costItemId, costValue, callbacks);
|
||||||
table.appendChild(tr);
|
table.appendChild(tr);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -496,9 +581,6 @@ export class CostUI {
|
|||||||
const addButton = CostUI.createAddCostValueButton(costItemId, callbacks);
|
const addButton = CostUI.createAddCostValueButton(costItemId, callbacks);
|
||||||
form.appendChild(addButton);
|
form.appendChild(addButton);
|
||||||
|
|
||||||
const submitButton = CostUI.createSubmitButton(callbacks);
|
|
||||||
form.appendChild(submitButton);
|
|
||||||
|
|
||||||
const closeButton = CostUI.createCloseButton(form);
|
const closeButton = CostUI.createCloseButton(form);
|
||||||
form.appendChild(closeButton);
|
form.appendChild(closeButton);
|
||||||
|
|
||||||
@@ -514,16 +596,15 @@ export class CostUI {
|
|||||||
return document.getElementById("cost-values-table-" + costItemId);
|
return document.getElementById("cost-values-table-" + costItemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
static addNewCostValueRow(costItemId, costValueId) {
|
static addNewCostValueRow(costItemId, costValueId, costValueCallbacks) {
|
||||||
const table = CostUI.getCostValuesTable(costItemId);
|
const table = CostUI.getCostValuesTable(costItemId);
|
||||||
// check if table exists
|
|
||||||
if (!table) {
|
if (!table) {
|
||||||
console.log("Cost values table not found for cost item ID:", costItemId);
|
console.log("Cost values table not found for cost item ID:", costItemId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("Adding new cost value row", costValueId);
|
console.log("Adding new cost value row", costValueId);
|
||||||
console.log(table)
|
console.log(table)
|
||||||
const tr = CostUI.createCostvaluesRow(costItemId, {"category": "", "name": "", "applied_value": 0, "id": costValueId, "parent": costItemId});
|
const tr = CostUI.createCostvaluesRow(costItemId, {"category": "", "name": "", "applied_value": 0, "id": costValueId, "parent": costItemId}, costValueCallbacks);
|
||||||
table.appendChild(tr);
|
table.appendChild(tr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -543,19 +624,14 @@ export class CostUI {
|
|||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createCostvaluesRow(costItemId,costValue) {
|
static createCostvaluesRow(costItemId,costValue, costValueCallbacks) {
|
||||||
|
|
||||||
function cleanLabel(value) {
|
function cleanLabel(value) {
|
||||||
// remove anything which is not a dot or a digit
|
|
||||||
return value.replace(/[^0-9.]/g, '');
|
return value.replace(/[^0-9.]/g, '');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (document.getElementById(costValue.id)) {
|
if (document.getElementById(costValue.id)) {
|
||||||
return document.getElementById(costValue.id);
|
return document.getElementById(costValue.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tr = document.createElement("tr");
|
const tr = document.createElement("tr");
|
||||||
tr.isCostValue = true;
|
tr.isCostValue = true;
|
||||||
tr.parent = costItemId;
|
tr.parent = costItemId;
|
||||||
@@ -577,6 +653,24 @@ export class CostUI {
|
|||||||
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);
|
||||||
|
|
||||||
|
if (selectedType === "SUM") {
|
||||||
|
const costValueId = parseInt(this.closest("tr").getAttribute("id"));
|
||||||
|
const appliedValue = parseFloat(valueCell1.querySelector("input").value);
|
||||||
|
const costCategory = categoryCell.querySelector("input").value;
|
||||||
|
|
||||||
|
if (costValueCallbacks.editCostValues) {
|
||||||
|
costValueCallbacks.editCostValues(costItemId, [
|
||||||
|
{
|
||||||
|
costType: selectedType,
|
||||||
|
costCategory: costCategory,
|
||||||
|
appliedValue: appliedValue,
|
||||||
|
id: costValueId,
|
||||||
|
costItemId: costItemId
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
tr.appendChild(typeCell);
|
tr.appendChild(typeCell);
|
||||||
|
|
||||||
@@ -593,9 +687,40 @@ export class CostUI {
|
|||||||
}
|
}
|
||||||
const valueCell1 = CostUI.createTableInput("number", "value", value);
|
const valueCell1 = CostUI.createTableInput("number", "value", value);
|
||||||
tr.appendChild(valueCell1);
|
tr.appendChild(valueCell1);
|
||||||
// Apply initial state based on costType
|
|
||||||
CostUI.updateRowBasedOnType(costType, categoryCell, valueCell1);
|
|
||||||
|
|
||||||
|
const inputValue = valueCell1.querySelector("input");
|
||||||
|
function handleCostValueChange(costItemId) {
|
||||||
|
const costValueData = {
|
||||||
|
costType: typeCell.querySelector("select").value,
|
||||||
|
costCategory: categoryCell.querySelector("input").value,
|
||||||
|
appliedValue: parseFloat(this.value),
|
||||||
|
id: costValue.id,
|
||||||
|
costItemId: costItemId
|
||||||
|
}
|
||||||
|
if (costValueCallbacks.editCostValues) {
|
||||||
|
costValueCallbacks.editCostValues(costItemId, [costValueData]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inputValue.addEventListener("change", handleCostValueChange.bind(inputValue, costItemId));
|
||||||
|
|
||||||
|
inputValue.addEventListener("keydown", function(event) {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
handleCostValueChange.call(this, costItemId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const deleteCell = document.createElement("td");
|
||||||
|
tr.appendChild(deleteCell);
|
||||||
|
const deleteButton = document.createElement("button");
|
||||||
|
deleteButton.classList.add("action-button");
|
||||||
|
deleteButton.textContent = "Delete";
|
||||||
|
deleteButton.addEventListener("click", function() {
|
||||||
|
costValueCallbacks.deleteCostValue(costItemId, costValue.id);
|
||||||
|
tr.remove();
|
||||||
|
});
|
||||||
|
deleteCell.appendChild(deleteButton);
|
||||||
|
CostUI.updateRowBasedOnType(costType, categoryCell, valueCell1);
|
||||||
return tr;
|
return tr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -650,8 +775,8 @@ export class CostUI {
|
|||||||
|
|
||||||
static createAddCostValueButton(costItemId, callbacks) {
|
static createAddCostValueButton(costItemId, callbacks) {
|
||||||
const addButton = document.createElement("button");
|
const addButton = document.createElement("button");
|
||||||
addButton.textContent = "+";
|
addButton.classList.add("action-button");
|
||||||
addButton.classList.add("add-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;
|
||||||
@@ -659,38 +784,11 @@ export class CostUI {
|
|||||||
return addButton;
|
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) {
|
static createFormElement(id, className) {
|
||||||
const form = document.createElement("form");
|
const form = document.createElement("form");
|
||||||
form.id = id;
|
form.id = id;
|
||||||
form.classList.add(className);
|
form.classList.add(className);
|
||||||
form.style.position = "absolute"; // Set initial position to absolute
|
form.style.position = "absolute";
|
||||||
form.style.top = "50%";
|
form.style.top = "50%";
|
||||||
form.style.left = "50%";
|
form.style.left = "50%";
|
||||||
form.style.transform = "translate(-50%, -50%)";
|
form.style.transform = "translate(-50%, -50%)";
|
||||||
@@ -710,7 +808,10 @@ export class CostUI {
|
|||||||
closeButton.innerHTML = "×";
|
closeButton.innerHTML = "×";
|
||||||
closeButton.addEventListener("click", function() {
|
closeButton.addEventListener("click", function() {
|
||||||
form.remove();
|
form.remove();
|
||||||
|
const costItemId = form.id.split("-")[3];
|
||||||
|
CostUI.unhighlightCostItem(costItemId);
|
||||||
});
|
});
|
||||||
|
|
||||||
return closeButton;
|
return closeButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<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/gantt.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"
|
||||||
@@ -32,30 +32,6 @@
|
|||||||
<script type="module" defer src="./static/js/cost.js"></script>
|
<script type="module" defer src="./static/js/cost.js"></script>
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
.nested {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.expand-collapse {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.caret-cell {
|
|
||||||
border: none;
|
|
||||||
background: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
td {
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
.flex-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -73,7 +49,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/costing" class="active"
|
<a href="/costing" class="active"
|
||||||
><i class="fa-solid fa-table"></i>Costing</a
|
><i class="fa-solid fa-money-bill-trend-up"></i> Costing</a
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -364,6 +364,15 @@ class Web(bonsai.core.tool.Web):
|
|||||||
bpy.ops.bim.add_cost_item(cost_item=operator_data["costItemId"])
|
bpy.ops.bim.add_cost_item(cost_item=operator_data["costItemId"])
|
||||||
cost_schedule = tool.Cost.get_cost_schedule(cost_item=tool.Ifc.get().by_id(operator_data["costItemId"]))
|
cost_schedule = tool.Cost.get_cost_schedule(cost_item=tool.Ifc.get().by_id(operator_data["costItemId"]))
|
||||||
cls.load_cost_schedule_web_ui(cost_schedule)
|
cls.load_cost_schedule_web_ui(cost_schedule)
|
||||||
|
if operator_data["type"] == "deleteCostItem":
|
||||||
|
cost_schedule = tool.Cost.get_cost_schedule(cost_item=tool.Ifc.get().by_id(operator_data["costItemId"]))
|
||||||
|
bpy.ops.bim.remove_cost_item(cost_item=operator_data["costItemId"])
|
||||||
|
cls.load_cost_schedule_web_ui(cost_schedule)
|
||||||
|
if operator_data["type"] == "duplicateCostItem":
|
||||||
|
cost_item = tool.Ifc.get().by_id(operator_data["costItemId"])
|
||||||
|
cost_schedule = tool.Cost.get_cost_schedule(cost_item=cost_item)
|
||||||
|
new_cost_item = ifcopenshell.api.cost.copy_cost_item(ifc_file, cost_item)
|
||||||
|
cls.load_cost_schedule_web_ui(cost_schedule)
|
||||||
if operator_data["type"] == "editCostItemName":
|
if operator_data["type"] == "editCostItemName":
|
||||||
cost_item = tool.Ifc.get().by_id(operator_data["costItemId"])
|
cost_item = tool.Ifc.get().by_id(operator_data["costItemId"])
|
||||||
tool.Ifc.run(
|
tool.Ifc.run(
|
||||||
@@ -391,18 +400,25 @@ class Web(bonsai.core.tool.Web):
|
|||||||
data_key="cost_value",
|
data_key="cost_value",
|
||||||
event="cost_value"
|
event="cost_value"
|
||||||
)
|
)
|
||||||
|
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"])
|
||||||
|
cost_schedule = tool.Cost.get_cost_schedule(cost_item=cost_item)
|
||||||
|
cls.load_cost_schedule_web_ui(cost_schedule)
|
||||||
if operator_data["type"] == "editCostValues":
|
if operator_data["type"] == "editCostValues":
|
||||||
cost_item_id = operator_data["costItemId"]
|
cost_item_id = operator_data["costItemId"]
|
||||||
print('Editing cost values', cost_item_id)
|
cost_item = ifc_file.by_id(cost_item_id)
|
||||||
print(cost_item_id)
|
cost_schedule = tool.Cost.get_cost_schedule(cost_item=cost_item)
|
||||||
print(type(operator_data["costValues"]))
|
|
||||||
for value_data in operator_data["costValues"] or []:
|
for value_data in operator_data["costValues"] or []:
|
||||||
print(value_data)
|
print("editing cost value data", value_data)
|
||||||
value = ifc_file.by_id(value_data["id"])
|
value = ifc_file.by_id(value_data["id"])
|
||||||
print(value.get_info())
|
|
||||||
if value_data["costType"] == "FIXED":
|
if value_data["costType"] == "FIXED":
|
||||||
attributes= {
|
attributes= {
|
||||||
"AppliedValue": value_data["appliedValue"],
|
"AppliedValue": value_data["appliedValue"],
|
||||||
|
"Category": None
|
||||||
}
|
}
|
||||||
elif value_data["costType"] == "CATEGORY":
|
elif value_data["costType"] == "CATEGORY":
|
||||||
attributes= {
|
attributes= {
|
||||||
@@ -418,12 +434,8 @@ class Web(bonsai.core.tool.Web):
|
|||||||
cost_value=value,
|
cost_value=value,
|
||||||
attributes= attributes
|
attributes= attributes
|
||||||
)
|
)
|
||||||
|
print(value.get_info())
|
||||||
tool.Cost.load_cost_schedule_tree()
|
tool.Cost.load_cost_schedule_tree()
|
||||||
cost_item = ifc_file.by_id(operator_data["costItemId"])
|
|
||||||
if not cost_item:
|
|
||||||
print("Cost item not found")
|
|
||||||
return
|
|
||||||
cost_schedule = tool.Cost.get_cost_schedule(cost_item=cost_item)
|
|
||||||
cls.load_cost_schedule_web_ui(cost_schedule)
|
cls.load_cost_schedule_web_ui(cost_schedule)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user