feat: implement accordion for historical analysis with real data

- Add expandable rows in historical analysis table
- Implement accordion functionality with chevron icons
- Load real CPU and memory data from API endpoint
- Display current, average, and peak usage with progress bars
- Show recommendations based on historical data
- Improve UX with inline details instead of separate cards
This commit is contained in:
2025-10-02 11:54:07 -03:00
parent 53ea070957
commit 05a54b7855

View File

@@ -516,6 +516,33 @@
.section-hidden { .section-hidden {
display: none !important; display: none !important;
} }
/* Accordion Styles */
.expand-button {
background: none;
border: none;
color: var(--pf-global--Color--100);
cursor: pointer;
padding: 8px;
border-radius: 4px;
transition: all 0.2s ease;
}
.expand-button:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.workload-details-row {
background-color: #1A1A1A;
}
.workload-details-container {
padding: 0;
}
.workload-row:hover {
background-color: rgba(255, 255, 255, 0.02);
}
</style> </style>
</head> </head>
<body> <body>
@@ -910,6 +937,7 @@
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th style="width: 30px;"></th>
<th>Workload</th> <th>Workload</th>
<th>Namespace</th> <th>Namespace</th>
<th>Pods</th> <th>Pods</th>
@@ -920,8 +948,13 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
${data.workloads.map(workload => ` ${data.workloads.map((workload, index) => `
<tr> <tr class="workload-row" data-workload="${workload.name}" data-namespace="${workload.namespace}">
<td>
<button class="expand-button" onclick="toggleWorkloadDetails(${index})" id="expand-btn-${index}">
<i class="fas fa-chevron-right"></i>
</button>
</td>
<td> <td>
<strong style="color: var(--pf-global--Color--100);">${workload.name}</strong> <strong style="color: var(--pf-global--Color--100);">${workload.name}</strong>
</td> </td>
@@ -931,12 +964,22 @@
<td>${workload.memory_usage || 'N/A'}</td> <td>${workload.memory_usage || 'N/A'}</td>
<td>${workload.last_updated ? new Date(workload.last_updated).toLocaleString() : 'N/A'}</td> <td>${workload.last_updated ? new Date(workload.last_updated).toLocaleString() : 'N/A'}</td>
<td> <td>
<button class="openshift-button" onclick="showWorkloadDetails('${workload.name}', '${workload.namespace}')"> <button class="openshift-button" onclick="loadWorkloadDetails('${workload.name}', '${workload.namespace}', ${index})">
<i class="fas fa-eye"></i> <i class="fas fa-chart-line"></i>
View Details Load Details
</button> </button>
</td> </td>
</tr> </tr>
<tr class="workload-details-row" id="details-${index}" style="display: none;">
<td colspan="8">
<div class="workload-details-container" id="details-content-${index}">
<div class="loading-spinner">
<div class="spinner"></div>
Loading workload details...
</div>
</div>
</td>
</tr>
`).join('')} `).join('')}
</tbody> </tbody>
</table> </table>
@@ -970,6 +1013,169 @@
} }
} }
function toggleWorkloadDetails(index) {
const detailsRow = document.getElementById(`details-${index}`);
const expandBtn = document.getElementById(`expand-btn-${index}`);
const icon = expandBtn.querySelector('i');
if (detailsRow.style.display === 'none') {
detailsRow.style.display = 'table-row';
icon.classList.remove('fa-chevron-right');
icon.classList.add('fa-chevron-down');
} else {
detailsRow.style.display = 'none';
icon.classList.remove('fa-chevron-down');
icon.classList.add('fa-chevron-right');
}
}
async function loadWorkloadDetails(workloadName, namespace, index) {
const container = document.getElementById(`details-content-${index}`);
try {
container.innerHTML = `
<div class="loading-spinner">
<div class="spinner"></div>
Loading workload details...
</div>
`;
const response = await fetch(`/api/v1/historical-analysis/${namespace}/${workloadName}`);
const data = await response.json();
updateWorkloadDetailsAccordion(data, index);
} catch (error) {
console.error('Error loading workload details:', error);
container.innerHTML = `
<div style="text-align: center; padding: 40px; color: var(--pf-global--danger-color--100);">
<i class="fas fa-exclamation-triangle" style="font-size: 48px; margin-bottom: 16px;"></i>
<h3 style="margin: 0 0 8px 0; color: var(--pf-global--Color--100);">Error</h3>
<p style="margin: 0;">Failed to load workload details</p>
</div>
`;
}
}
function updateWorkloadDetailsAccordion(data, index) {
const container = document.getElementById(`details-content-${index}`);
// Parse CPU and Memory data
const cpuData = data.cpu_data || {};
const memoryData = data.memory_data || {};
const recommendations = data.recommendations || [];
container.innerHTML = `
<div style="padding: 24px; background-color: #1E1E1E; border-radius: 8px; margin: 16px 0;">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 24px;">
<div class="openshift-card">
<div class="card-header">
<h3 class="card-title">
<i class="fas fa-microchip" style="margin-right: 8px; color: var(--pf-global--info-color--100);"></i>
CPU Usage
</h3>
</div>
<div style="padding: 20px;">
${cpuData.current ? `
<div style="margin-bottom: 16px;">
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
<span>Current Usage:</span>
<strong style="color: var(--pf-global--Color--100);">${cpuData.current} cores</strong>
</div>
<div style="background-color: #404040; height: 8px; border-radius: 4px; overflow: hidden;">
<div style="background-color: var(--pf-global--info-color--100); height: 100%; width: ${Math.min((cpuData.current / (cpuData.limit || 1)) * 100, 100)}%;"></div>
</div>
</div>
` : ''}
${cpuData.average ? `
<div style="margin-bottom: 16px;">
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
<span>Average (24h):</span>
<strong style="color: var(--pf-global--Color--100);">${cpuData.average} cores</strong>
</div>
</div>
` : ''}
${cpuData.peak ? `
<div style="margin-bottom: 16px;">
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
<span>Peak (24h):</span>
<strong style="color: var(--pf-global--warning-color--100);">${cpuData.peak} cores</strong>
</div>
</div>
` : ''}
${!cpuData.current && !cpuData.average && !cpuData.peak ? `
<div style="text-align: center; color: var(--pf-global--Color--300);">
<i class="fas fa-chart-line" style="font-size: 32px; margin-bottom: 8px; color: var(--pf-global--Color--400);"></i>
<p>CPU usage data not available</p>
</div>
` : ''}
</div>
</div>
<div class="openshift-card">
<div class="card-header">
<h3 class="card-title">
<i class="fas fa-memory" style="margin-right: 8px; color: var(--pf-global--warning-color--100);"></i>
Memory Usage
</h3>
</div>
<div style="padding: 20px;">
${memoryData.current ? `
<div style="margin-bottom: 16px;">
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
<span>Current Usage:</span>
<strong style="color: var(--pf-global--Color--100);">${memoryData.current}</strong>
</div>
<div style="background-color: #404040; height: 8px; border-radius: 4px; overflow: hidden;">
<div style="background-color: var(--pf-global--warning-color--100); height: 100%; width: ${Math.min((memoryData.current_bytes / (memoryData.limit_bytes || 1)) * 100, 100)}%;"></div>
</div>
</div>
` : ''}
${memoryData.average ? `
<div style="margin-bottom: 16px;">
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
<span>Average (24h):</span>
<strong style="color: var(--pf-global--Color--100);">${memoryData.average}</strong>
</div>
</div>
` : ''}
${memoryData.peak ? `
<div style="margin-bottom: 16px;">
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
<span>Peak (24h):</span>
<strong style="color: var(--pf-global--danger-color--100);">${memoryData.peak}</strong>
</div>
</div>
` : ''}
${!memoryData.current && !memoryData.average && !memoryData.peak ? `
<div style="text-align: center; color: var(--pf-global--Color--300);">
<i class="fas fa-chart-line" style="font-size: 32px; margin-bottom: 8px; color: var(--pf-global--Color--400);"></i>
<p>Memory usage data not available</p>
</div>
` : ''}
</div>
</div>
</div>
${recommendations.length > 0 ? `
<div class="openshift-card" style="margin-top: 24px;">
<div class="card-header">
<h3 class="card-title">
<i class="fas fa-lightbulb" style="margin-right: 8px; color: var(--pf-global--success-color--100);"></i>
Recommendations
</h3>
</div>
<div style="padding: 20px;">
<ul style="margin: 0; padding-left: 20px;">
${recommendations.map(rec => `<li style="margin-bottom: 8px; color: var(--pf-global--Color--200);">${rec}</li>`).join('')}
</ul>
</div>
</div>
` : ''}
</div>
`;
}
function updateWorkloadDetails(data) { function updateWorkloadDetails(data) {
const container = document.getElementById('workload-details-content'); const container = document.getElementById('workload-details-content');
@@ -982,21 +1188,21 @@
<div style="padding: 20px; text-align: center; color: var(--pf-global--Color--300);"> <div style="padding: 20px; text-align: center; color: var(--pf-global--Color--300);">
<i class="fas fa-microchip" style="font-size: 48px; margin-bottom: 16px; color: var(--pf-global--info-color--100);"></i> <i class="fas fa-microchip" style="font-size: 48px; margin-bottom: 16px; color: var(--pf-global--info-color--100);"></i>
<p>CPU usage data will be displayed here</p> <p>CPU usage data will be displayed here</p>
</div> </div>
</div> </div>
<div class="openshift-card"> <div class="openshift-card">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Memory Usage</h3> <h3 class="card-title">Memory Usage</h3>
</div> </div>
<div style="padding: 20px; text-align: center; color: var(--pf-global--Color--300);"> <div style="padding: 20px; text-align: center; color: var(--pf-global--Color--300);">
<i class="fas fa-memory" style="font-size: 48px; margin-bottom: 16px; color: var(--pf-global--warning-color--100);"></i> <i class="fas fa-memory" style="font-size: 48px; margin-bottom: 16px; color: var(--pf-global--warning-color--100);"></i>
<p>Memory usage data will be displayed here</p> <p>Memory usage data will be displayed here</p>
</div> </div>
</div> </div>
</div> </div>
`; `;
} }
function analyzeWorkload(namespace) { function analyzeWorkload(namespace) {
console.log('Analyzing workload:', namespace); console.log('Analyzing workload:', namespace);
// TODO: Implement workload analysis // TODO: Implement workload analysis