378 lines
11 KiB
HTML
378 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Pennyzo API Status Report Viewer</title>
|
|
<style>
|
|
:root {
|
|
--deep-navy: #05174d;
|
|
--brass: #d4af37;
|
|
--off-white: #f5f5f5;
|
|
--light-gray: #e0e0e0;
|
|
--success: #28a745;
|
|
--danger: #dc3545;
|
|
--warning: #ffc107;
|
|
--info: #17a2b8;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
margin: 0;
|
|
padding: 0;
|
|
background-color: var(--off-white);
|
|
color: #333;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
|
|
header {
|
|
background-color: var(--deep-navy);
|
|
color: white;
|
|
padding: 20px;
|
|
text-align: center;
|
|
border-bottom: 4px solid var(--brass);
|
|
}
|
|
|
|
h1 {
|
|
margin: 0;
|
|
font-size: 2em;
|
|
}
|
|
|
|
.report-selector {
|
|
margin: 20px 0;
|
|
padding: 15px;
|
|
background-color: white;
|
|
border-radius: 5px;
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.summary {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 15px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.summary-card {
|
|
background-color: white;
|
|
border-radius: 5px;
|
|
padding: 15px;
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
|
text-align: center;
|
|
}
|
|
|
|
.summary-card h3 {
|
|
margin-top: 0;
|
|
color: var(--deep-navy);
|
|
}
|
|
|
|
.summary-card .value {
|
|
font-size: 2em;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.success-rate {
|
|
color: var(--success);
|
|
}
|
|
|
|
.table-container {
|
|
overflow-x: auto;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
background-color: white;
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
th, td {
|
|
padding: 12px 15px;
|
|
text-align: left;
|
|
border-bottom: 1px solid var(--light-gray);
|
|
}
|
|
|
|
th {
|
|
background-color: var(--deep-navy);
|
|
color: white;
|
|
position: sticky;
|
|
top: 0;
|
|
}
|
|
|
|
tr:hover {
|
|
background-color: var(--light-gray);
|
|
}
|
|
|
|
.status {
|
|
padding: 5px 10px;
|
|
border-radius: 3px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.status-success {
|
|
background-color: var(--success);
|
|
color: white;
|
|
}
|
|
|
|
.status-failed {
|
|
background-color: var(--danger);
|
|
color: white;
|
|
}
|
|
|
|
.method {
|
|
font-weight: bold;
|
|
}
|
|
|
|
.method-GET {
|
|
color: var(--info);
|
|
}
|
|
|
|
.method-POST {
|
|
color: var(--warning);
|
|
}
|
|
|
|
.method-PUT {
|
|
color: var(--deep-navy);
|
|
}
|
|
|
|
.method-DELETE {
|
|
color: var(--danger);
|
|
}
|
|
|
|
.details-row {
|
|
display: none;
|
|
background-color: #f9f9f9;
|
|
}
|
|
|
|
.details-cell {
|
|
padding: 15px;
|
|
}
|
|
|
|
.details-container {
|
|
white-space: pre-wrap;
|
|
font-family: monospace;
|
|
background-color: #f5f5f5;
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
overflow-x: auto;
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.expandable {
|
|
cursor: pointer;
|
|
}
|
|
|
|
.expandable::after {
|
|
content: '▼';
|
|
margin-left: 5px;
|
|
font-size: 0.8em;
|
|
}
|
|
|
|
footer {
|
|
margin-top: 30px;
|
|
text-align: center;
|
|
padding: 10px;
|
|
color: #666;
|
|
}
|
|
|
|
#file-input {
|
|
display: none;
|
|
}
|
|
|
|
.file-upload-btn {
|
|
background-color: var(--deep-navy);
|
|
color: white;
|
|
padding: 10px 15px;
|
|
border: none;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
margin-right: 10px;
|
|
}
|
|
|
|
.file-name {
|
|
margin-left: 10px;
|
|
font-style: italic;
|
|
}
|
|
|
|
.no-report {
|
|
padding: 50px;
|
|
text-align: center;
|
|
background-color: white;
|
|
border-radius: 5px;
|
|
margin-top: 20px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<h1>Pennyzo API Status Report Viewer</h1>
|
|
</header>
|
|
|
|
<div class="container">
|
|
<div class="report-selector">
|
|
<h2>Select Report</h2>
|
|
<input type="file" id="file-input" accept=".json">
|
|
<button class="file-upload-btn" onclick="document.getElementById('file-input').click()">
|
|
Choose Report File
|
|
</button>
|
|
<span class="file-name" id="file-name"></span>
|
|
</div>
|
|
|
|
<div id="report-content">
|
|
<div class="no-report">
|
|
<h2>No Report Loaded</h2>
|
|
<p>Please select a report JSON file to view its contents.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<footer>
|
|
<p>Pennyzo API Status Report Viewer | © 2025 Pennyzo</p>
|
|
</footer>
|
|
</div>
|
|
|
|
<script>
|
|
document.getElementById('file-input').addEventListener('change', function(event) {
|
|
const file = event.target.files[0];
|
|
if (file) {
|
|
document.getElementById('file-name').textContent = file.name;
|
|
|
|
const reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
try {
|
|
const report = JSON.parse(e.target.result);
|
|
renderReport(report);
|
|
} catch (error) {
|
|
alert('Error parsing JSON file: ' + error.message);
|
|
}
|
|
};
|
|
reader.readAsText(file);
|
|
}
|
|
});
|
|
|
|
function renderReport(report) {
|
|
const reportContent = document.getElementById('report-content');
|
|
|
|
// Create HTML for report
|
|
let html = `
|
|
<h2>${report.title}</h2>
|
|
<p><strong>Generated:</strong> ${new Date(report.timestamp).toLocaleString()}</p>
|
|
<p><strong>Duration:</strong> ${report.duration}</p>
|
|
<p><strong>Base URL:</strong> ${report.baseUrl}</p>
|
|
|
|
<div class="summary">
|
|
<div class="summary-card">
|
|
<h3>Total Endpoints</h3>
|
|
<div class="value">${report.summary.totalEndpoints}</div>
|
|
</div>
|
|
<div class="summary-card">
|
|
<h3>Successful</h3>
|
|
<div class="value" style="color: var(--success)">${report.summary.successful}</div>
|
|
</div>
|
|
<div class="summary-card">
|
|
<h3>Failed</h3>
|
|
<div class="value" style="color: var(--danger)">${report.summary.failed}</div>
|
|
</div>
|
|
<div class="summary-card">
|
|
<h3>Success Rate</h3>
|
|
<div class="value success-rate">${report.summary.successRate}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<h2>Endpoint Results</h2>
|
|
<div class="table-container">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Endpoint</th>
|
|
<th>Method</th>
|
|
<th>Status</th>
|
|
<th>Status Code</th>
|
|
<th>Response Time</th>
|
|
<th>Timestamp</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
`;
|
|
|
|
// Add rows for each result
|
|
report.results.forEach((result, index) => {
|
|
html += `
|
|
<tr class="expandable" onclick="toggleDetails(${index})">
|
|
<td>${result.endpoint}</td>
|
|
<td><span class="method method-${result.method}">${result.method}</span></td>
|
|
<td><span class="status status-${result.status.toLowerCase()}">${result.status}</span></td>
|
|
<td>${result.statusCode}</td>
|
|
<td>${result.responseTime}ms</td>
|
|
<td>${new Date(result.timestamp).toLocaleTimeString()}</td>
|
|
</tr>
|
|
<tr id="details-${index}" class="details-row">
|
|
<td colspan="6" class="details-cell">
|
|
<div class="details-container">
|
|
${renderDetails(result)}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
`;
|
|
});
|
|
|
|
html += `
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
`;
|
|
|
|
reportContent.innerHTML = html;
|
|
}
|
|
|
|
function renderDetails(result) {
|
|
let details = '';
|
|
|
|
if (result.error) {
|
|
details += `<strong>Error Message:</strong> ${result.error.message}\n\n`;
|
|
if (result.error.details) {
|
|
details += `<strong>Error Details:</strong>\n${JSON.stringify(result.error.details, null, 2)}\n\n`;
|
|
}
|
|
}
|
|
|
|
if (result.data) {
|
|
details += `<strong>Response Data:</strong>\n${JSON.stringify(result.data, null, 2)}`;
|
|
}
|
|
|
|
return details || 'No additional details available';
|
|
}
|
|
|
|
function toggleDetails(index) {
|
|
const detailsRow = document.getElementById(`details-${index}`);
|
|
if (detailsRow.style.display === 'table-row') {
|
|
detailsRow.style.display = 'none';
|
|
} else {
|
|
detailsRow.style.display = 'table-row';
|
|
}
|
|
}
|
|
|
|
// Load example report on page load
|
|
fetch('example-report.json')
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Example report not found');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(report => {
|
|
document.getElementById('file-name').textContent = 'example-report.json (pre-loaded)';
|
|
renderReport(report);
|
|
})
|
|
.catch(error => {
|
|
console.log('No example report pre-loaded:', error);
|
|
// No need to show an error, we'll just show the default "No Report Loaded" message
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |