315 lines
9.6 KiB
HTML
315 lines
9.6 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>QC Report - Job {{ job_number }}</title>
|
|
<style>
|
|
.print-notice {
|
|
background: #e3f2fd;
|
|
border: 2px solid #2196f3;
|
|
padding: 15px;
|
|
margin-bottom: 20px;
|
|
border-radius: 5px;
|
|
}
|
|
@media print {
|
|
.print-notice { display: none; }
|
|
}
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
margin: 20px;
|
|
color: #333;
|
|
}
|
|
h1, h2, h3 {
|
|
color: #2c3e50;
|
|
}
|
|
.header {
|
|
border-bottom: 3px solid #3498db;
|
|
padding-bottom: 10px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.summary {
|
|
background-color: #f8f9fa;
|
|
padding: 15px;
|
|
border-radius: 5px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.summary-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 15px;
|
|
margin-top: 10px;
|
|
}
|
|
.summary-item {
|
|
text-align: center;
|
|
padding: 10px;
|
|
background: white;
|
|
border-radius: 5px;
|
|
}
|
|
.summary-value {
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
display: block;
|
|
}
|
|
.summary-label {
|
|
font-size: 12px;
|
|
color: #666;
|
|
text-transform: uppercase;
|
|
}
|
|
.file-section {
|
|
page-break-inside: avoid;
|
|
margin-bottom: 30px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 5px;
|
|
padding: 15px;
|
|
}
|
|
.file-header {
|
|
background-color: #3498db;
|
|
color: white;
|
|
padding: 10px;
|
|
margin: -15px -15px 15px -15px;
|
|
border-radius: 5px 5px 0 0;
|
|
}
|
|
.check-item {
|
|
margin-bottom: 15px;
|
|
padding: 10px;
|
|
border-left: 4px solid #ddd;
|
|
background-color: #f8f9fa;
|
|
}
|
|
.check-item.passed {
|
|
border-left-color: #27ae60;
|
|
}
|
|
.check-item.error {
|
|
border-left-color: #e74c3c;
|
|
background-color: #fce4e4;
|
|
}
|
|
.check-item.warning {
|
|
border-left-color: #f39c12;
|
|
background-color: #fff8e1;
|
|
}
|
|
.status-badge {
|
|
display: inline-block;
|
|
padding: 3px 8px;
|
|
border-radius: 3px;
|
|
font-size: 11px;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
}
|
|
.status-passed {
|
|
background-color: #27ae60;
|
|
color: white;
|
|
}
|
|
.status-error {
|
|
background-color: #e74c3c;
|
|
color: white;
|
|
}
|
|
.status-warning {
|
|
background-color: #f39c12;
|
|
color: white;
|
|
}
|
|
.status-skipped {
|
|
background-color: #95a5a6;
|
|
color: white;
|
|
}
|
|
.check-details {
|
|
margin-top: 8px;
|
|
font-size: 12px;
|
|
}
|
|
.check-details strong {
|
|
color: #2c3e50;
|
|
}
|
|
.error-list {
|
|
background-color: #fff;
|
|
border: 1px solid #e74c3c;
|
|
padding: 10px;
|
|
margin-top: 15px;
|
|
border-radius: 5px;
|
|
}
|
|
.footer {
|
|
margin-top: 30px;
|
|
padding-top: 15px;
|
|
border-top: 2px solid #ddd;
|
|
font-size: 11px;
|
|
color: #666;
|
|
text-align: center;
|
|
}
|
|
.errors-only-banner {
|
|
background-color: #fce4e4;
|
|
border: 2px solid #e74c3c;
|
|
padding: 15px;
|
|
margin-bottom: 20px;
|
|
border-radius: 5px;
|
|
text-align: center;
|
|
}
|
|
.errors-only-banner h3 {
|
|
color: #e74c3c;
|
|
margin: 0 0 5px 0;
|
|
}
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-top: 10px;
|
|
}
|
|
table td {
|
|
padding: 5px;
|
|
border-bottom: 1px solid #ddd;
|
|
}
|
|
table td:first-child {
|
|
font-weight: bold;
|
|
width: 30%;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="print-notice">
|
|
<p><strong>💡 Tip:</strong> To save as PDF, use your browser's Print function (Ctrl/Cmd + P) and select "Save as PDF"</p>
|
|
</div>
|
|
|
|
<div class="header">
|
|
<h1>QC Report Summary</h1>
|
|
<h2>Job Number: {{ job_number }}</h2>
|
|
<p><strong>Generated:</strong> {{ generated_at }}</p>
|
|
</div>
|
|
|
|
{% if errors_only %}
|
|
<div class="errors-only-banner">
|
|
<h3>⚠️ ERROR REPORTS ONLY</h3>
|
|
<p>This export contains only files with errors. Files that passed all checks are excluded.</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="summary">
|
|
<h3>Overall Summary</h3>
|
|
<div class="summary-grid">
|
|
<div class="summary-item">
|
|
<span class="summary-value">{{ aggregated.total_files }}</span>
|
|
<span class="summary-label">Files Checked</span>
|
|
</div>
|
|
<div class="summary-item">
|
|
<span class="summary-value">{{ aggregated.total_checks }}</span>
|
|
<span class="summary-label">Total Checks</span>
|
|
</div>
|
|
<div class="summary-item">
|
|
<span class="summary-value" style="color: #27ae60;">{{ aggregated.summary.passed }}</span>
|
|
<span class="summary-label">Passed</span>
|
|
</div>
|
|
<div class="summary-item">
|
|
<span class="summary-value" style="color: #e74c3c;">{{ aggregated.summary.error }}</span>
|
|
<span class="summary-label">Errors</span>
|
|
</div>
|
|
<div class="summary-item">
|
|
<span class="summary-value" style="color: #f39c12;">{{ aggregated.summary.warning }}</span>
|
|
<span class="summary-label">Warnings</span>
|
|
</div>
|
|
<div class="summary-item">
|
|
<span class="summary-value" style="color: #95a5a6;">{{ aggregated.summary.skipped }}</span>
|
|
<span class="summary-label">Skipped</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if aggregated.files_with_errors %}
|
|
<div class="error-list">
|
|
<h3 style="color: #e74c3c;">Files with Errors ({{ aggregated.files_with_errors|length }})</h3>
|
|
<ul>
|
|
{% for file in aggregated.files_with_errors %}
|
|
<li>
|
|
<strong>{{ file.filename }}</strong> - {{ file.error_count }} error(s)
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<h2 style="margin-top: 30px;">Detailed Reports</h2>
|
|
|
|
{% for report in reports %}
|
|
<div class="file-section">
|
|
<div class="file-header">
|
|
<h3 style="margin: 0;">{{ report.filename }}</h3>
|
|
{% if report.timestamp %}
|
|
<small>Generated: {{ report.timestamp }}</small>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if report.error %}
|
|
<div class="check-item error">
|
|
<strong>Error:</strong> {{ report.error }}
|
|
</div>
|
|
{% else %}
|
|
<table>
|
|
<tr>
|
|
<td>Total Checks:</td>
|
|
<td>{{ report.summary.total }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Passed:</td>
|
|
<td style="color: #27ae60;">{{ report.summary.passed }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Errors:</td>
|
|
<td style="color: #e74c3c;">{{ report.summary.error }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Warnings:</td>
|
|
<td style="color: #f39c12;">{{ report.summary.warning }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Skipped:</td>
|
|
<td style="color: #95a5a6;">{{ report.summary.skipped }}</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<h4 style="margin-top: 20px;">Check Results:</h4>
|
|
|
|
{% for check in report.checks %}
|
|
<div class="check-item {{ check.status }}">
|
|
<div>
|
|
<span class="status-badge status-{{ check.status }}">{{ check.status_text }}</span>
|
|
<strong>{{ check.name }}</strong>
|
|
</div>
|
|
|
|
{% if check.description %}
|
|
<div style="margin-top: 5px; color: #666; font-size: 12px;">
|
|
{{ check.description }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if check.error_message %}
|
|
<div style="margin-top: 10px; color: #e74c3c; font-weight: bold;">
|
|
Error: {{ check.error_message }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if check.results %}
|
|
<div class="check-details">
|
|
<strong>Results:</strong>
|
|
<ul style="margin: 5px 0; padding-left: 20px;">
|
|
{% for key, value in check.results.items() %}
|
|
<li>
|
|
<strong>{{ key }}:</strong>
|
|
{% if value is mapping %}
|
|
<ul style="margin-left: 20px;">
|
|
{% for k, v in value.items() %}
|
|
<li>{{ k }}: {{ v }}</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% else %}
|
|
{{ value }}
|
|
{% endif %}
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
|
|
<div class="footer">
|
|
<p>QC Report Dashboard - Generated {{ generated_at }}</p>
|
|
<p>This report was automatically generated from Box.com QC reports</p>
|
|
</div>
|
|
</body>
|
|
</html>
|