From 602a3d6215585c23e99d8ea58e868e56d56a0ee0 Mon Sep 17 00:00:00 2001 From: Patrick Balsiger Date: Sun, 28 Sep 2025 13:41:44 +0200 Subject: [PATCH] feat: boolean param in dynamic form --- .../components/MonitoringViewComponent.js | 27 ++++++---- .../components/NodeDetailsComponent.js | 26 +++++++++- public/styles/main.css | 51 +++++++++++++++++++ 3 files changed, 92 insertions(+), 12 deletions(-) diff --git a/public/scripts/components/MonitoringViewComponent.js b/public/scripts/components/MonitoringViewComponent.js index 23023ea..d34e120 100644 --- a/public/scripts/components/MonitoringViewComponent.js +++ b/public/scripts/components/MonitoringViewComponent.js @@ -375,12 +375,14 @@ class MonitoringViewComponent extends Component { return `
-
${hostname || ip}
+
+
+ 🔴 +
+
${hostname || ip}
+
${ip}
-
- ❌ No Resources -
${labels && Object.keys(labels).length > 0 ? `
@@ -453,18 +455,23 @@ class MonitoringViewComponent extends Component { storageUtilization = storageTotal > 0 ? Math.round((storageUsed / storageTotal) * 100) : 0; } - const resourceSourceText = resourceSource === 'monitoring' ? '📊 Full Monitoring' : - resourceSource === 'basic' ? '📋 Basic Data' : '❓ Unknown'; + // Determine status indicator based on resource source + const statusIcon = resourceSource === 'monitoring' ? '🟢' : + resourceSource === 'basic' ? '🟡' : '🔴'; + const statusClass = resourceSource === 'monitoring' ? 'status-online' : + resourceSource === 'basic' ? 'status-warning' : 'status-offline'; return `
-
${hostname || ip}
+
+
+ ${statusIcon} +
+
${hostname || ip}
+
${ip}
-
- ${resourceSourceText} -
${labels && Object.keys(labels).length > 0 ? `
diff --git a/public/scripts/components/NodeDetailsComponent.js b/public/scripts/components/NodeDetailsComponent.js index 484950b..103e912 100644 --- a/public/scripts/components/NodeDetailsComponent.js +++ b/public/scripts/components/NodeDetailsComponent.js @@ -99,12 +99,29 @@ class NodeDetailsComponent extends Component { value="${defaultValue}">`; } + renderBooleanComponent(p, formId, pidx) { + const defaultValue = p.default !== undefined ? p.default : false; + const checked = defaultValue ? 'checked' : ''; + return `
+ + +
`; + } + // Component map for parameter types getParameterComponentMap() { const components = { select: this.renderSelectComponent, color: this.renderColorComponent, numberRange: this.renderNumberRangeComponent, + boolean: this.renderBooleanComponent, text: this.renderTextComponent }; @@ -119,7 +136,8 @@ class NodeDetailsComponent extends Component { const typeRules = [ { condition: () => Array.isArray(p.values) && p.values.length > 1, type: 'select' }, { condition: () => p.type === 'color', type: 'color' }, - { condition: () => p.type === 'numberRange', type: 'numberRange' } + { condition: () => p.type === 'numberRange', type: 'numberRange' }, + { condition: () => p.type === 'boolean', type: 'boolean' } ]; const matchedRule = typeRules.find(rule => rule.condition()); @@ -616,6 +634,10 @@ class NodeDetailsComponent extends Component { const rgbDisplay = input.parentElement.querySelector('.color-rgb-display'); value = rgbDisplay ? rgbDisplay.value : this.hexToRgbInt(input.value); } + // For boolean type, convert checkbox checked state to boolean + else if (input.dataset.paramType === 'boolean' && input.type === 'checkbox') { + value = input.checked; + } return { name: input.dataset.paramName, location: input.dataset.paramLocation || 'body', @@ -626,7 +648,7 @@ class NodeDetailsComponent extends Component { }); // Required validation - const missing = params.filter(p => p.required && (!p.value || String(p.value).trim() === '')); + const missing = params.filter(p => p.required && (p.type === 'boolean' ? false : (!p.value || String(p.value).trim() === ''))); if (missing.length > 0) { resultEl.style.display = 'block'; resultEl.innerHTML = ` diff --git a/public/styles/main.css b/public/styles/main.css index 07cf3a7..f87cf1f 100644 --- a/public/styles/main.css +++ b/public/styles/main.css @@ -749,6 +749,7 @@ p { background: rgba(76, 175, 80, 0.3); color: var(--accent-success); border: 1px solid rgba(76, 175, 80, 0.5); + border-radius: 50%; } .status-offline { @@ -2423,6 +2424,56 @@ select.param-input:focus { background-size: 12px 12px; } +/* Boolean checkbox styling */ +.boolean-input-container { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.param-input.boolean-checkbox { + width: auto; + padding: 0; + margin: 0; + background: transparent; + border: 1px solid var(--border-primary); + border-radius: 4px; + width: 16px; + height: 16px; + appearance: none; + cursor: pointer; + position: relative; + transition: all 0.2s ease; +} + +.param-input.boolean-checkbox:checked { + background: var(--accent-primary); + border-color: var(--accent-primary); +} + +.param-input.boolean-checkbox:checked::after { + content: '✓'; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: white; + font-size: 10px; + font-weight: bold; +} + +.param-input.boolean-checkbox:focus { + border-color: rgba(139, 92, 246, 0.5); + box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.15); +} + +.boolean-label { + font-size: 0.9rem; + color: var(--text-primary); + cursor: pointer; + user-select: none; +} + .endpoint-params.none { opacity: 1; font-size: 0.85rem;