feat: boolean param in dynamic form

This commit is contained in:
2025-09-28 13:41:44 +02:00
parent 85505586ac
commit 602a3d6215
3 changed files with 92 additions and 12 deletions

View File

@@ -375,12 +375,14 @@ class MonitoringViewComponent extends Component {
return ` return `
<div class="node-card error" data-node-ip="${ip}"> <div class="node-card error" data-node-ip="${ip}">
<div class="node-header"> <div class="node-header">
<div class="node-title">${hostname || ip}</div> <div class="status-hostname-group">
<div class="node-status-indicator status-offline">
🔴
</div>
<div class="node-title">${hostname || ip}</div>
</div>
<div class="node-ip">${ip}</div> <div class="node-ip">${ip}</div>
</div> </div>
<div class="node-status">
<span class="status-badge error">❌ No Resources</span>
</div>
${labels && Object.keys(labels).length > 0 ? ` ${labels && Object.keys(labels).length > 0 ? `
<div class="node-labels"> <div class="node-labels">
@@ -453,18 +455,23 @@ class MonitoringViewComponent extends Component {
storageUtilization = storageTotal > 0 ? Math.round((storageUsed / storageTotal) * 100) : 0; storageUtilization = storageTotal > 0 ? Math.round((storageUsed / storageTotal) * 100) : 0;
} }
const resourceSourceText = resourceSource === 'monitoring' ? '📊 Full Monitoring' : // Determine status indicator based on resource source
resourceSource === 'basic' ? '📋 Basic Data' : '❓ Unknown'; const statusIcon = resourceSource === 'monitoring' ? '🟢' :
resourceSource === 'basic' ? '🟡' : '🔴';
const statusClass = resourceSource === 'monitoring' ? 'status-online' :
resourceSource === 'basic' ? 'status-warning' : 'status-offline';
return ` return `
<div class="node-card" data-node-ip="${ip}"> <div class="node-card" data-node-ip="${ip}">
<div class="node-header"> <div class="node-header">
<div class="node-title">${hostname || ip}</div> <div class="status-hostname-group">
<div class="node-status-indicator ${statusClass}">
${statusIcon}
</div>
<div class="node-title">${hostname || ip}</div>
</div>
<div class="node-ip">${ip}</div> <div class="node-ip">${ip}</div>
</div> </div>
<div class="node-status">
<span class="status-badge ${resourceSource === 'monitoring' ? 'success' : 'warning'}">${resourceSourceText}</span>
</div>
${labels && Object.keys(labels).length > 0 ? ` ${labels && Object.keys(labels).length > 0 ? `
<div class="node-labels"> <div class="node-labels">

View File

@@ -99,12 +99,29 @@ class NodeDetailsComponent extends Component {
value="${defaultValue}">`; value="${defaultValue}">`;
} }
renderBooleanComponent(p, formId, pidx) {
const defaultValue = p.default !== undefined ? p.default : false;
const checked = defaultValue ? 'checked' : '';
return `<div class="boolean-input-container">
<input id="${formId}-field-${pidx}"
data-param-name="${p.name}"
data-param-location="${p.location || 'body'}"
data-param-type="${p.type || 'boolean'}"
data-param-required="${p.required ? '1' : '0'}"
class="param-input boolean-checkbox"
type="checkbox"
${checked}>
<label for="${formId}-field-${pidx}" class="boolean-label">${p.name}</label>
</div>`;
}
// Component map for parameter types // Component map for parameter types
getParameterComponentMap() { getParameterComponentMap() {
const components = { const components = {
select: this.renderSelectComponent, select: this.renderSelectComponent,
color: this.renderColorComponent, color: this.renderColorComponent,
numberRange: this.renderNumberRangeComponent, numberRange: this.renderNumberRangeComponent,
boolean: this.renderBooleanComponent,
text: this.renderTextComponent text: this.renderTextComponent
}; };
@@ -119,7 +136,8 @@ class NodeDetailsComponent extends Component {
const typeRules = [ const typeRules = [
{ condition: () => Array.isArray(p.values) && p.values.length > 1, type: 'select' }, { condition: () => Array.isArray(p.values) && p.values.length > 1, type: 'select' },
{ condition: () => p.type === 'color', type: 'color' }, { 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()); const matchedRule = typeRules.find(rule => rule.condition());
@@ -616,6 +634,10 @@ class NodeDetailsComponent extends Component {
const rgbDisplay = input.parentElement.querySelector('.color-rgb-display'); const rgbDisplay = input.parentElement.querySelector('.color-rgb-display');
value = rgbDisplay ? rgbDisplay.value : this.hexToRgbInt(input.value); 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 { return {
name: input.dataset.paramName, name: input.dataset.paramName,
location: input.dataset.paramLocation || 'body', location: input.dataset.paramLocation || 'body',
@@ -626,7 +648,7 @@ class NodeDetailsComponent extends Component {
}); });
// Required validation // 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) { if (missing.length > 0) {
resultEl.style.display = 'block'; resultEl.style.display = 'block';
resultEl.innerHTML = ` resultEl.innerHTML = `

View File

@@ -749,6 +749,7 @@ p {
background: rgba(76, 175, 80, 0.3); background: rgba(76, 175, 80, 0.3);
color: var(--accent-success); color: var(--accent-success);
border: 1px solid rgba(76, 175, 80, 0.5); border: 1px solid rgba(76, 175, 80, 0.5);
border-radius: 50%;
} }
.status-offline { .status-offline {
@@ -2423,6 +2424,56 @@ select.param-input:focus {
background-size: 12px 12px; 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 { .endpoint-params.none {
opacity: 1; opacity: 1;
font-size: 0.85rem; font-size: 0.85rem;