diff --git a/app/static/index.html b/app/static/index.html index f64c56a..417274d 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -543,6 +543,102 @@ .workload-row:hover { background-color: rgba(255, 255, 255, 0.02); } + + /* Modal Styles */ + .modal { + display: none; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.8); + overflow-y: auto; + } + + .modal-content { + background-color: var(--pf-global--BackgroundColor--100); + margin: 2% auto; + padding: 0; + border-radius: 8px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5); + animation: modalSlideIn 0.3s ease-out; + } + + @keyframes modalSlideIn { + from { + opacity: 0; + transform: translateY(-50px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + + .modal-header { + background: linear-gradient(135deg, #2B2B2B 0%, #1E1E1E 100%); + color: var(--pf-global--Color--100); + padding: 20px 24px; + border-bottom: 1px solid #404040; + display: flex; + justify-content: space-between; + align-items: center; + border-radius: 8px 8px 0 0; + } + + .modal-header h2 { + margin: 0; + font-size: 20px; + font-weight: 600; + } + + .close { + color: var(--pf-global--Color--300); + font-size: 28px; + font-weight: bold; + cursor: pointer; + transition: color 0.2s ease; + } + + .close:hover { + color: var(--pf-global--Color--100); + } + + .modal-body { + padding: 24px; + max-height: 70vh; + overflow-y: auto; + } + + .status-indicator { + padding: 4px 8px; + border-radius: 4px; + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + } + + .status-indicator.danger { + background-color: var(--pf-global--danger-color--100); + color: white; + } + + .status-indicator.warning { + background-color: var(--pf-global--warning-color--100); + color: black; + } + + .status-indicator.info { + background-color: var(--pf-global--info-color--100); + color: white; + } + + .status-indicator.success { + background-color: var(--pf-global--success-color--100); + color: white; + } @@ -897,20 +993,20 @@ ${namespace.namespace} - ${Object.keys(namespace.pods || {}).length} - ${namespace.total_validations || 0} + ${Object.keys(namespace.pods || {}).length} + ${namespace.total_validations || 0} ${getSeverityText(namespace)} - - - + + `).join('')} @@ -1203,9 +1299,147 @@ `; } - function analyzeWorkload(namespace) { - console.log('Analyzing workload:', namespace); - // TODO: Implement workload analysis + function analyzeNamespace(namespaceName) { + if (!currentData || !currentData.validations || !currentData.validations.namespaces) return; + + const namespace = currentData.validations.namespaces.find(ns => ns.namespace === namespaceName); + if (!namespace) return; + + // Show details in modal + showNamespaceDetails(namespaceName); + } + + function showNamespaceDetails(namespaceName) { + // Create modal if it doesn't exist + let modal = document.getElementById('namespaceModal'); + if (!modal) { + modal = document.createElement('div'); + modal.id = 'namespaceModal'; + modal.className = 'modal'; + modal.innerHTML = ` + + `; + document.body.appendChild(modal); + + // Add close functionality + modal.querySelector('.close').onclick = () => modal.style.display = 'none'; + modal.onclick = (e) => { + if (e.target === modal) modal.style.display = 'none'; + }; + } + + // Create detailed content + const namespace = currentData.validations.namespaces.find(ns => ns.namespace === namespaceName); + if (!namespace) return; + + let content = ` +
+
+

📊 Summary

+
+
+ Pods: ${Object.keys(namespace.pods || {}).length} +
+
+ Total Issues: ${namespace.total_validations || 0} +
+
+ Errors: ${namespace.severity_breakdown?.error || 0} +
+
+ Warnings: ${namespace.severity_breakdown?.warning || 0} +
+
+
+
+

🔍 Pod Analysis

+ `; + + // Add details for each pod + Object.values(namespace.pods || {}).forEach(pod => { + content += ` +
+

📦 ${pod.pod_name}

+
+
Status: ${pod.phase}
+
Node: ${pod.node_name}
+
+
+
Containers:
+ `; + + pod.containers.forEach(container => { + const hasRequests = Object.keys(container.resources?.requests || {}).length > 0; + const hasLimits = Object.keys(container.resources?.limits || {}).length > 0; + + content += ` +
+
${container.name}
+

Image: ${container.image}

+
+
+ Requests: + ${hasRequests ? + `${JSON.stringify(container.resources.requests)}` : + '❌ Not defined' + } +
+
+ Limits: + ${hasLimits ? + `${JSON.stringify(container.resources.limits)}` : + '❌ Not defined' + } +
+
+
+ `; + }); + + content += ` +
+
+
Issues Found:
+ `; + + if (pod.validations && pod.validations.length > 0) { + pod.validations.forEach(validation => { + const severityClass = validation.severity === 'error' ? 'danger' : + validation.severity === 'warning' ? 'warning' : 'info'; + const severityColor = validation.severity === 'error' ? 'var(--pf-global--danger-color--100)' : + validation.severity === 'warning' ? 'var(--pf-global--warning-color--100)' : 'var(--pf-global--info-color--100)'; + + content += ` +
+

${validation.rule_name}: ${validation.message}

+

Recommendation: ${validation.recommendation}

+
+ `; + }); + } else { + content += `

✅ No issues found for this pod.

`; + } + + content += ` +
+
+ `; + }); + + content += ` +
+
+ `; + + // Populate and show modal + document.getElementById('modalBody').innerHTML = content; + modal.style.display = 'block'; } function getSeverityClass(namespace) {