Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Add /admin/portal-seo to run SEO audits on nordabiznes.pl using the same SEOAuditor used for company websites. Tracks results over time for before/after comparison. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
287 lines
10 KiB
HTML
287 lines
10 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}SEO Portalu - Norda Biznes Partner{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.portal-seo-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: var(--spacing-xl);
|
|
flex-wrap: wrap;
|
|
gap: var(--spacing-md);
|
|
}
|
|
.score-cards {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
|
gap: var(--spacing-md);
|
|
margin-bottom: var(--spacing-xl);
|
|
}
|
|
.score-card {
|
|
background: white;
|
|
border-radius: var(--radius-lg);
|
|
padding: var(--spacing-lg);
|
|
text-align: center;
|
|
box-shadow: var(--shadow-sm);
|
|
border: 1px solid var(--border);
|
|
}
|
|
.score-card-value {
|
|
font-size: 2.5rem;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
margin-bottom: var(--spacing-xs);
|
|
}
|
|
.score-card-label {
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-secondary);
|
|
}
|
|
.score-card-delta {
|
|
font-size: var(--font-size-sm);
|
|
margin-top: var(--spacing-xs);
|
|
}
|
|
.score-good { color: var(--success); }
|
|
.score-ok { color: var(--warning); }
|
|
.score-bad { color: var(--error); }
|
|
.delta-up { color: var(--success); }
|
|
.delta-down { color: var(--error); }
|
|
.delta-same { color: var(--text-secondary); }
|
|
|
|
.check-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: var(--spacing-sm);
|
|
margin-bottom: var(--spacing-xl);
|
|
}
|
|
.check-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-sm);
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
background: white;
|
|
border-radius: var(--radius);
|
|
border: 1px solid var(--border);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
.check-pass { border-left: 3px solid var(--success); }
|
|
.check-fail { border-left: 3px solid var(--error); }
|
|
.check-na { border-left: 3px solid var(--border); }
|
|
|
|
.audit-history {
|
|
background: white;
|
|
border-radius: var(--radius-lg);
|
|
overflow: hidden;
|
|
box-shadow: var(--shadow-sm);
|
|
border: 1px solid var(--border);
|
|
}
|
|
.audit-history table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
}
|
|
.audit-history th {
|
|
background: #f8fafc;
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
text-align: left;
|
|
font-size: var(--font-size-sm);
|
|
font-weight: 600;
|
|
color: var(--text-secondary);
|
|
border-bottom: 2px solid var(--border);
|
|
}
|
|
.audit-history td {
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
border-bottom: 1px solid var(--border);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
.audit-history tr:hover td {
|
|
background: #f8fafc;
|
|
}
|
|
.score-badge {
|
|
display: inline-block;
|
|
padding: 2px 8px;
|
|
border-radius: 4px;
|
|
font-weight: 600;
|
|
font-size: 12px;
|
|
}
|
|
.score-badge.good { background: #d1fae5; color: #065f46; }
|
|
.score-badge.ok { background: #fef3c7; color: #92400e; }
|
|
.score-badge.bad { background: #fee2e2; color: #991b1b; }
|
|
|
|
.run-form {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="portal-seo-header">
|
|
<div>
|
|
<h1 style="margin-bottom: var(--spacing-xs);">SEO Portalu</h1>
|
|
<p style="color: var(--text-secondary);">Audyt {{ portal_url }} - historia zmian w czasie</p>
|
|
</div>
|
|
<form method="POST" action="{{ url_for('admin.admin_portal_seo_run') }}" class="run-form">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<input type="text" name="notes" placeholder="Notatka (opcjonalnie)" style="padding: 8px 12px; border: 1px solid var(--border); border-radius: var(--radius); font-size: var(--font-size-sm); width: 200px;">
|
|
<button type="submit" class="btn btn-primary" style="white-space: nowrap;">
|
|
Uruchom audyt
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
{% if audits %}
|
|
{% set latest = audits[0] %}
|
|
{% set prev = audits[1] if audits|length > 1 else None %}
|
|
|
|
<!-- Latest scores -->
|
|
<h2 style="font-size: var(--font-size-lg); margin-bottom: var(--spacing-md); color: var(--text-secondary);">
|
|
Ostatni audyt: {{ latest.audited_at.strftime('%d.%m.%Y %H:%M') }}
|
|
</h2>
|
|
|
|
<div class="score-cards">
|
|
{% macro score_card(label, value, prev_value) %}
|
|
<div class="score-card">
|
|
<div class="score-card-value {{ 'score-good' if value and value >= 90 else ('score-ok' if value and value >= 50 else 'score-bad') }}">
|
|
{{ value if value is not none else '—' }}
|
|
</div>
|
|
<div class="score-card-label">{{ label }}</div>
|
|
{% if prev_value is not none and value is not none %}
|
|
{% set delta = value - prev_value %}
|
|
<div class="score-card-delta {{ 'delta-up' if delta > 0 else ('delta-down' if delta < 0 else 'delta-same') }}">
|
|
{{ '+' if delta > 0 else '' }}{{ delta }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endmacro %}
|
|
|
|
{{ score_card('Performance', latest.pagespeed_performance, prev.pagespeed_performance if prev else None) }}
|
|
{{ score_card('SEO', latest.pagespeed_seo, prev.pagespeed_seo if prev else None) }}
|
|
{{ score_card('Accessibility', latest.pagespeed_accessibility, prev.pagespeed_accessibility if prev else None) }}
|
|
{{ score_card('Best Practices', latest.pagespeed_best_practices, prev.pagespeed_best_practices if prev else None) }}
|
|
{{ score_card('On-Page', latest.on_page_score, prev.on_page_score if prev else None) }}
|
|
{{ score_card('Technical', latest.technical_score, prev.technical_score if prev else None) }}
|
|
</div>
|
|
|
|
<!-- Key checks -->
|
|
<h3 style="margin-bottom: var(--spacing-md);">Kluczowe elementy SEO</h3>
|
|
<div class="check-grid">
|
|
{% macro check_item(label, value) %}
|
|
<div class="check-item {{ 'check-pass' if value == true else ('check-fail' if value == false else 'check-na') }}">
|
|
{% if value == true %}
|
|
<span style="color: var(--success);">✓</span>
|
|
{% elif value == false %}
|
|
<span style="color: var(--error);">✗</span>
|
|
{% else %}
|
|
<span style="color: var(--text-secondary);">?</span>
|
|
{% endif %}
|
|
{{ label }}
|
|
</div>
|
|
{% endmacro %}
|
|
|
|
{{ check_item('robots.txt', latest.has_robots_txt) }}
|
|
{{ check_item('sitemap.xml', latest.has_sitemap) }}
|
|
{{ check_item('Canonical URL', latest.has_canonical) }}
|
|
{{ check_item('Structured Data', latest.has_structured_data) }}
|
|
{{ check_item('Open Graph', latest.has_og_tags) }}
|
|
{{ check_item('SSL', latest.has_ssl) }}
|
|
{{ check_item('Mobile Friendly', latest.is_mobile_friendly) }}
|
|
{{ check_item('HSTS', latest.has_hsts) }}
|
|
{{ check_item('CSP', latest.has_csp) }}
|
|
</div>
|
|
|
|
<!-- Core Web Vitals -->
|
|
{% if latest.lcp_ms or latest.fcp_ms or latest.cls is not none %}
|
|
<h3 style="margin-bottom: var(--spacing-md);">Core Web Vitals</h3>
|
|
<div class="score-cards" style="margin-bottom: var(--spacing-xl);">
|
|
{% if latest.lcp_ms %}
|
|
<div class="score-card">
|
|
<div class="score-card-value {{ 'score-good' if latest.lcp_ms <= 2500 else ('score-ok' if latest.lcp_ms <= 4000 else 'score-bad') }}" style="font-size: 1.8rem;">
|
|
{{ '%.1f'|format(latest.lcp_ms / 1000) }}s
|
|
</div>
|
|
<div class="score-card-label">LCP</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if latest.fcp_ms %}
|
|
<div class="score-card">
|
|
<div class="score-card-value {{ 'score-good' if latest.fcp_ms <= 1800 else ('score-ok' if latest.fcp_ms <= 3000 else 'score-bad') }}" style="font-size: 1.8rem;">
|
|
{{ '%.1f'|format(latest.fcp_ms / 1000) }}s
|
|
</div>
|
|
<div class="score-card-label">FCP</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if latest.cls is not none %}
|
|
<div class="score-card">
|
|
<div class="score-card-value {{ 'score-good' if latest.cls <= 0.1 else ('score-ok' if latest.cls <= 0.25 else 'score-bad') }}" style="font-size: 1.8rem;">
|
|
{{ '%.3f'|format(latest.cls) }}
|
|
</div>
|
|
<div class="score-card-label">CLS</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if latest.tbt_ms %}
|
|
<div class="score-card">
|
|
<div class="score-card-value {{ 'score-good' if latest.tbt_ms <= 200 else ('score-ok' if latest.tbt_ms <= 600 else 'score-bad') }}" style="font-size: 1.8rem;">
|
|
{{ latest.tbt_ms|int }}ms
|
|
</div>
|
|
<div class="score-card-label">TBT</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
<!-- History table -->
|
|
<h3 style="margin-bottom: var(--spacing-md);">Historia audytów</h3>
|
|
{% if audits %}
|
|
<div class="audit-history">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Data</th>
|
|
<th>Perf</th>
|
|
<th>SEO</th>
|
|
<th>A11y</th>
|
|
<th>BP</th>
|
|
<th>On-Page</th>
|
|
<th>Tech</th>
|
|
<th>Notatka</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for a in audits %}
|
|
<tr>
|
|
<td>{{ a.audited_at.strftime('%d.%m.%Y %H:%M') }}</td>
|
|
{% macro score_td(val) %}
|
|
<td>
|
|
{% if val is not none %}
|
|
<span class="score-badge {{ 'good' if val >= 90 else ('ok' if val >= 50 else 'bad') }}">{{ val }}</span>
|
|
{% else %}—{% endif %}
|
|
</td>
|
|
{% endmacro %}
|
|
{{ score_td(a.pagespeed_performance) }}
|
|
{{ score_td(a.pagespeed_seo) }}
|
|
{{ score_td(a.pagespeed_accessibility) }}
|
|
{{ score_td(a.pagespeed_best_practices) }}
|
|
{{ score_td(a.on_page_score) }}
|
|
{{ score_td(a.technical_score) }}
|
|
<td style="max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
|
|
{{ a.notes or '' }}
|
|
</td>
|
|
<td>
|
|
<a href="{{ url_for('admin.admin_portal_seo_detail', audit_id=a.id) }}" style="color: var(--primary); font-size: var(--font-size-sm);">
|
|
Szczegóły
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div style="text-align: center; padding: var(--spacing-2xl); color: var(--text-secondary);">
|
|
<p>Brak audytów. Kliknij "Uruchom audyt" aby wykonać pierwszy audyt SEO portalu.</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% endblock %}
|