487 lines
14 KiB
HTML
487 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Component Health Report - OpenShift {{RELEASE}}</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
padding: 20px;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
background: white;
|
|
border-radius: 12px;
|
|
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.header {
|
|
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
|
color: white;
|
|
padding: 40px;
|
|
text-align: center;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 2.5em;
|
|
margin-bottom: 10px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.header .subtitle {
|
|
font-size: 1.1em;
|
|
opacity: 0.9;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.content {
|
|
padding: 40px;
|
|
}
|
|
|
|
.section {
|
|
margin-bottom: 50px;
|
|
}
|
|
|
|
.section h2 {
|
|
color: #2c3e50;
|
|
font-size: 1.8em;
|
|
margin-bottom: 20px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 3px solid #667eea;
|
|
}
|
|
|
|
.metrics-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
gap: 20px;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.metric-card {
|
|
background: #f8f9fa;
|
|
border-radius: 8px;
|
|
padding: 25px;
|
|
border-left: 5px solid #667eea;
|
|
transition: transform 0.2s, box-shadow 0.2s;
|
|
}
|
|
|
|
.metric-card:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.metric-card.poor {
|
|
border-left-color: #e74c3c;
|
|
background: #fee;
|
|
}
|
|
|
|
.metric-card.warning {
|
|
border-left-color: #f39c12;
|
|
background: #fff8e1;
|
|
}
|
|
|
|
.metric-card.good {
|
|
border-left-color: #27ae60;
|
|
background: #e8f5e9;
|
|
}
|
|
|
|
.metric-label {
|
|
font-size: 0.9em;
|
|
color: #666;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.metric-value {
|
|
font-size: 2.5em;
|
|
font-weight: 700;
|
|
color: #2c3e50;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.metric-assessment {
|
|
font-size: 1.1em;
|
|
font-weight: 600;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.metric-details {
|
|
font-size: 0.9em;
|
|
color: #555;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-top: 20px;
|
|
background: white;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
thead {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white;
|
|
}
|
|
|
|
th {
|
|
padding: 15px;
|
|
text-align: left;
|
|
font-weight: 600;
|
|
font-size: 0.9em;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
td {
|
|
padding: 12px 15px;
|
|
border-bottom: 1px solid #ecf0f1;
|
|
}
|
|
|
|
tbody tr:hover {
|
|
background: #f8f9fa;
|
|
}
|
|
|
|
tbody tr:last-child td {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.grade-excellent {
|
|
color: #27ae60;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.grade-good {
|
|
color: #3498db;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.grade-warning {
|
|
color: #f39c12;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.grade-poor {
|
|
color: #e74c3c;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.alert-box {
|
|
background: #fee;
|
|
border-left: 5px solid #e74c3c;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.alert-box h3 {
|
|
color: #c0392b;
|
|
margin-bottom: 15px;
|
|
font-size: 1.3em;
|
|
}
|
|
|
|
.alert-box ul {
|
|
list-style-position: inside;
|
|
color: #555;
|
|
}
|
|
|
|
.alert-box li {
|
|
margin-bottom: 8px;
|
|
padding-left: 10px;
|
|
}
|
|
|
|
.insight-box {
|
|
background: #e8f5e9;
|
|
border-left: 5px solid #27ae60;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.insight-box h3 {
|
|
color: #27ae60;
|
|
margin-bottom: 15px;
|
|
font-size: 1.3em;
|
|
}
|
|
|
|
.insight-box ul {
|
|
list-style-position: inside;
|
|
color: #555;
|
|
}
|
|
|
|
.insight-box li {
|
|
margin-bottom: 8px;
|
|
padding-left: 10px;
|
|
}
|
|
|
|
.recommendation-box {
|
|
background: #fff8e1;
|
|
border-left: 5px solid #f39c12;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.recommendation-box h3 {
|
|
color: #e67e22;
|
|
margin-bottom: 15px;
|
|
font-size: 1.3em;
|
|
}
|
|
|
|
.recommendation-box ol {
|
|
list-style-position: inside;
|
|
color: #555;
|
|
}
|
|
|
|
.recommendation-box li {
|
|
margin-bottom: 10px;
|
|
padding-left: 10px;
|
|
}
|
|
|
|
.stats-row {
|
|
display: flex;
|
|
gap: 15px;
|
|
flex-wrap: wrap;
|
|
margin-top: 15px;
|
|
}
|
|
|
|
.stat-pill {
|
|
background: white;
|
|
padding: 8px 15px;
|
|
border-radius: 20px;
|
|
font-size: 0.9em;
|
|
border: 2px solid #ddd;
|
|
}
|
|
|
|
.stat-pill strong {
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.footer {
|
|
background: #f8f9fa;
|
|
padding: 20px 40px;
|
|
text-align: center;
|
|
color: #666;
|
|
font-size: 0.9em;
|
|
border-top: 1px solid #ddd;
|
|
}
|
|
|
|
.component-name {
|
|
font-weight: 600;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.filter-controls {
|
|
margin: 20px 0;
|
|
padding: 20px;
|
|
background: #f8f9fa;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.filter-controls label {
|
|
margin-right: 15px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.filter-controls input[type="text"] {
|
|
padding: 8px 12px;
|
|
border: 2px solid #ddd;
|
|
border-radius: 4px;
|
|
font-size: 1em;
|
|
width: 300px;
|
|
}
|
|
|
|
.filter-controls select {
|
|
padding: 8px 12px;
|
|
border: 2px solid #ddd;
|
|
border-radius: 4px;
|
|
font-size: 1em;
|
|
margin-left: 10px;
|
|
}
|
|
|
|
.hidden {
|
|
display: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>Component Health Report</h1>
|
|
<div class="subtitle">OpenShift {{RELEASE}} Release</div>
|
|
<div class="subtitle">{{RELEASE_PERIOD}}</div>
|
|
</div>
|
|
|
|
<div class="content">
|
|
<!-- Overall Health Section -->
|
|
<div class="section">
|
|
<h2>Overall Health Grade</h2>
|
|
|
|
<div class="metrics-grid">
|
|
<div class="metric-card {{TRIAGE_COVERAGE_CLASS}}">
|
|
<div class="metric-label">Triage Coverage</div>
|
|
<div class="metric-value">{{TRIAGE_COVERAGE}}%</div>
|
|
<div class="metric-assessment {{TRIAGE_COVERAGE_GRADE_CLASS}}">{{TRIAGE_COVERAGE_GRADE}}</div>
|
|
<div class="metric-details">
|
|
<div>Total: {{TOTAL_REGRESSIONS}} regressions</div>
|
|
<div>Triaged: {{TRIAGED_REGRESSIONS}}</div>
|
|
<div>Untriaged: {{UNTRIAGED_REGRESSIONS}}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="metric-card {{TRIAGE_TIME_CLASS}}">
|
|
<div class="metric-label">Triage Timeliness</div>
|
|
<div class="metric-value">{{TRIAGE_TIME_AVG}} hrs</div>
|
|
<div class="metric-assessment {{TRIAGE_TIME_GRADE_CLASS}}">{{TRIAGE_TIME_GRADE}}</div>
|
|
<div class="metric-details">
|
|
<div>Average: {{TRIAGE_TIME_AVG_DAYS}} days</div>
|
|
<div>Maximum: {{TRIAGE_TIME_MAX}} hrs ({{TRIAGE_TIME_MAX_DAYS}} days)</div>
|
|
<div>Target: <72 hours</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="metric-card {{RESOLUTION_TIME_CLASS}}">
|
|
<div class="metric-label">Resolution Speed</div>
|
|
<div class="metric-value">{{RESOLUTION_TIME_AVG}} hrs</div>
|
|
<div class="metric-assessment {{RESOLUTION_TIME_GRADE_CLASS}}">{{RESOLUTION_TIME_GRADE}}</div>
|
|
<div class="metric-details">
|
|
<div>Average: {{RESOLUTION_TIME_AVG_DAYS}} days</div>
|
|
<div>Maximum: {{RESOLUTION_TIME_MAX}} hrs ({{RESOLUTION_TIME_MAX_DAYS}} days)</div>
|
|
<div>Target: 1-2 weeks</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stats-row">
|
|
<div class="stat-pill">
|
|
<strong>Open:</strong> {{OPEN_REGRESSIONS}} regressions ({{OPEN_TRIAGE_PERCENTAGE}}% triaged)
|
|
</div>
|
|
<div class="stat-pill">
|
|
<strong>Closed:</strong> {{CLOSED_REGRESSIONS}} regressions ({{CLOSED_TRIAGE_PERCENTAGE}}% triaged)
|
|
</div>
|
|
<div class="stat-pill">
|
|
<strong>Avg Age (Open):</strong> {{OPEN_AGE_AVG}} hours ({{OPEN_AGE_AVG_DAYS}} days)
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Component Scorecard -->
|
|
<div class="section">
|
|
<h2>Per-Component Health Scorecard</h2>
|
|
|
|
<div class="filter-controls">
|
|
<label for="searchInput">Search Component:</label>
|
|
<input type="text" id="searchInput" placeholder="Type component name...">
|
|
|
|
<label for="gradeFilter">Filter by Grade:</label>
|
|
<select id="gradeFilter">
|
|
<option value="all">All Grades</option>
|
|
<option value="excellent">Excellent</option>
|
|
<option value="good">Good</option>
|
|
<option value="warning">Needs Improvement</option>
|
|
<option value="poor">Poor</option>
|
|
</select>
|
|
</div>
|
|
|
|
<table id="componentTable">
|
|
<thead>
|
|
<tr>
|
|
<th>Component</th>
|
|
<th>Total Regressions</th>
|
|
<th>Triage Coverage</th>
|
|
<th>Avg Triage Time</th>
|
|
<th>Avg Resolution Time</th>
|
|
<th>Open</th>
|
|
<th>Health Grade</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="componentTableBody">
|
|
{{COMPONENT_ROWS}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Critical Attention Section -->
|
|
<div class="section">
|
|
<h2>Components Needing Critical Attention</h2>
|
|
{{ATTENTION_SECTIONS}}
|
|
</div>
|
|
|
|
<!-- Key Insights -->
|
|
<div class="section">
|
|
<h2>Key Insights</h2>
|
|
<div class="insight-box">
|
|
<h3>📈 Analysis Summary</h3>
|
|
<ul>
|
|
{{INSIGHTS}}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recommendations -->
|
|
<div class="section">
|
|
<h2>Recommendations</h2>
|
|
<div class="recommendation-box">
|
|
<h3>🎯 Action Items</h3>
|
|
<ol>
|
|
{{RECOMMENDATIONS}}
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="footer">
|
|
<strong>Data Source:</strong> Sippy Component Readiness API<br>
|
|
<strong>Date Range:</strong> {{DATE_RANGE}}<br>
|
|
<strong>Total Regressions Analyzed:</strong> {{TOTAL_REGRESSIONS}}<br>
|
|
<strong>Generated:</strong> {{GENERATED_DATE}}
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Search functionality
|
|
const searchInput = document.getElementById('searchInput');
|
|
const gradeFilter = document.getElementById('gradeFilter');
|
|
const tableBody = document.getElementById('componentTableBody');
|
|
const rows = tableBody.getElementsByTagName('tr');
|
|
|
|
function filterTable() {
|
|
const searchTerm = searchInput.value.toLowerCase();
|
|
const gradeValue = gradeFilter.value;
|
|
|
|
for (let row of rows) {
|
|
const componentName = row.querySelector('.component-name').textContent.toLowerCase();
|
|
const grade = row.getAttribute('data-grade');
|
|
|
|
const matchesSearch = componentName.includes(searchTerm);
|
|
const matchesGrade = gradeValue === 'all' || grade === gradeValue;
|
|
|
|
if (matchesSearch && matchesGrade) {
|
|
row.style.display = '';
|
|
} else {
|
|
row.style.display = 'none';
|
|
}
|
|
}
|
|
}
|
|
|
|
searchInput.addEventListener('input', filterTable);
|
|
gradeFilter.addEventListener('change', filterTable);
|
|
</script>
|
|
</body>
|
|
</html>
|