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
When OG image fails to load (hotlink protection, CORS), shows clickable URL instead of just "Obrazek niedostepny". Adds referrerpolicy=no-referrer to bypass referer-based hotlink blocking. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1297 lines
67 KiB
HTML
1297 lines
67 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Audyt SEO: {{ company.name }} - Panel Admina{% endblock %}
|
||
|
||
{% block extra_css %}
|
||
<style>
|
||
.page-header {
|
||
display: flex; justify-content: space-between; align-items: flex-start;
|
||
margin-bottom: var(--spacing-xl); flex-wrap: wrap; gap: var(--spacing-md);
|
||
}
|
||
.page-header h1 { font-size: var(--font-size-2xl); margin-bottom: var(--spacing-xs); }
|
||
.page-header .subtitle { color: var(--text-secondary); font-size: var(--font-size-sm); }
|
||
.page-header .subtitle a { color: var(--primary); text-decoration: none; }
|
||
|
||
.back-link {
|
||
display: inline-flex; align-items: center; gap: var(--spacing-xs);
|
||
color: var(--text-secondary); text-decoration: none; font-size: var(--font-size-sm);
|
||
margin-bottom: var(--spacing-md);
|
||
}
|
||
.back-link:hover { color: var(--primary); }
|
||
|
||
/* Score cards */
|
||
.scores-grid {
|
||
display: grid; grid-template-columns: repeat(4, 1fr);
|
||
gap: var(--spacing-lg); margin-bottom: var(--spacing-2xl);
|
||
}
|
||
.score-card {
|
||
background: var(--surface); padding: var(--spacing-xl);
|
||
border-radius: var(--radius-lg); box-shadow: var(--shadow);
|
||
text-align: center;
|
||
}
|
||
.score-card .score-value {
|
||
font-size: 48px; font-weight: 700; line-height: 1;
|
||
margin-bottom: var(--spacing-xs);
|
||
}
|
||
.score-card .score-value.good { color: #16a34a; }
|
||
.score-card .score-value.medium { color: #d97706; }
|
||
.score-card .score-value.poor { color: #dc2626; }
|
||
.score-card .score-value.na { color: var(--text-muted); font-size: var(--font-size-2xl); }
|
||
.score-card .score-name { font-weight: 600; color: var(--text-primary); margin-bottom: 4px; }
|
||
.score-card .score-desc { font-size: var(--font-size-xs); color: var(--text-secondary); line-height: 1.4; }
|
||
.score-ring {
|
||
width: 100px; height: 100px; margin: 0 auto var(--spacing-md);
|
||
border-radius: 50%; display: flex; align-items: center; justify-content: center;
|
||
font-size: 32px; font-weight: 700;
|
||
}
|
||
.score-ring.good { background: #dcfce7; color: #16a34a; }
|
||
.score-ring.medium { background: #fef3c7; color: #d97706; }
|
||
.score-ring.poor { background: #fee2e2; color: #dc2626; }
|
||
.score-ring.na { background: var(--border); color: var(--text-muted); }
|
||
|
||
/* Sections */
|
||
.section {
|
||
background: var(--surface); padding: var(--spacing-xl);
|
||
border-radius: var(--radius-lg); box-shadow: var(--shadow);
|
||
margin-bottom: var(--spacing-xl);
|
||
}
|
||
.section h2 {
|
||
font-size: var(--font-size-lg); margin-bottom: var(--spacing-lg);
|
||
color: var(--text-primary); border-bottom: 2px solid var(--border);
|
||
padding-bottom: var(--spacing-sm);
|
||
}
|
||
|
||
/* Recommendations */
|
||
.reco-list { list-style: none; }
|
||
.reco-item {
|
||
padding: var(--spacing-md) var(--spacing-lg);
|
||
border-left: 4px solid var(--border);
|
||
margin-bottom: var(--spacing-sm);
|
||
border-radius: 0 var(--radius) var(--radius) 0;
|
||
background: var(--background);
|
||
font-size: var(--font-size-sm);
|
||
line-height: 1.5;
|
||
}
|
||
.reco-item.critical { border-left-color: #dc2626; background: #fef2f2; }
|
||
.reco-item.warning { border-left-color: #d97706; background: #fffbeb; }
|
||
.reco-item.info { border-left-color: #2563eb; background: #eff6ff; }
|
||
.reco-item.success { border-left-color: #16a34a; background: #f0fdf4; }
|
||
.reco-severity {
|
||
display: inline-block; padding: 1px 6px; border-radius: var(--radius-sm);
|
||
font-size: 10px; font-weight: 600; text-transform: uppercase;
|
||
margin-right: var(--spacing-sm); vertical-align: middle;
|
||
}
|
||
.reco-severity.critical { background: #dc2626; color: white; }
|
||
.reco-severity.warning { background: #d97706; color: white; }
|
||
.reco-severity.info { background: #2563eb; color: white; }
|
||
.reco-severity.success { background: #16a34a; color: white; }
|
||
|
||
/* Detail grid */
|
||
.detail-grid {
|
||
display: grid; grid-template-columns: 1fr 1fr;
|
||
gap: var(--spacing-md);
|
||
}
|
||
.detail-item {
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
padding: var(--spacing-sm) var(--spacing-md);
|
||
background: var(--background); border-radius: var(--radius);
|
||
}
|
||
.detail-label { color: var(--text-secondary); font-size: var(--font-size-sm); }
|
||
.detail-value { font-weight: 600; font-size: var(--font-size-sm); }
|
||
.detail-value.yes { color: #16a34a; }
|
||
.detail-value.no { color: #dc2626; }
|
||
|
||
/* Two columns */
|
||
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: var(--spacing-xl); }
|
||
|
||
/* Audit info bar */
|
||
.audit-info {
|
||
display: flex; gap: var(--spacing-lg); align-items: center;
|
||
padding: var(--spacing-md) var(--spacing-lg);
|
||
background: var(--background); border-radius: var(--radius);
|
||
margin-bottom: var(--spacing-xl); font-size: var(--font-size-sm);
|
||
color: var(--text-secondary); flex-wrap: wrap;
|
||
}
|
||
.audit-info strong { color: var(--text-primary); }
|
||
|
||
@media (max-width: 768px) {
|
||
.scores-grid { grid-template-columns: repeat(2, 1fr); }
|
||
.detail-grid { grid-template-columns: 1fr; }
|
||
.two-col { grid-template-columns: 1fr; }
|
||
}
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<a href="{{ url_for('admin.admin_seo') }}" class="back-link">
|
||
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
|
||
</svg>
|
||
Powrot do listy firm
|
||
</a>
|
||
|
||
<div class="page-header">
|
||
<div>
|
||
<h1>{{ company.name }}</h1>
|
||
<div class="subtitle">
|
||
{% if company.website %}
|
||
<a href="{{ company.website }}" target="_blank" rel="noopener">{{ company.website }}</a>
|
||
{% else %}
|
||
Brak strony WWW
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% if analysis and current_user.can_access_admin_panel() %}
|
||
<button class="btn btn-primary btn-sm" id="auditBtn" onclick="runAudit()">Uruchom audyt ponownie</button>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<!-- Stepper postepu audytu -->
|
||
<div id="auditProgress" style="display: none; margin-bottom: var(--spacing-lg); padding: var(--spacing-lg); background: var(--surface); border-radius: var(--radius); border: 1px solid var(--border);">
|
||
<strong style="display: block; margin-bottom: var(--spacing-md);">Audyt SEO — {{ company.name }}</strong>
|
||
<div id="auditSteps" style="display: flex; flex-direction: column; gap: 2px;">
|
||
<div class="audit-step" id="step-connect">
|
||
<span class="step-icon">○</span>
|
||
<span>Laczenie z Google PageSpeed Insights...</span>
|
||
</div>
|
||
<div class="audit-step" id="step-pagespeed">
|
||
<span class="step-icon">○</span>
|
||
<span>Analiza szybkosci i wydajnosci strony (PageSpeed)...</span>
|
||
</div>
|
||
<div class="audit-step" id="step-onpage">
|
||
<span class="step-icon">○</span>
|
||
<span>Sprawdzanie tresci, tagow i struktury HTML...</span>
|
||
</div>
|
||
<div class="audit-step" id="step-technical">
|
||
<span class="step-icon">○</span>
|
||
<span>Testy techniczne (SSL, robots.txt, sitemap)...</span>
|
||
</div>
|
||
<div class="audit-step" id="step-gsc">
|
||
<span class="step-icon">○</span>
|
||
<span>Pobieranie danych z Google Search Console...</span>
|
||
</div>
|
||
<div class="audit-step" id="step-save">
|
||
<span class="step-icon">○</span>
|
||
<span>Zapisywanie wynikow do bazy...</span>
|
||
</div>
|
||
<div class="audit-step" id="step-done">
|
||
<span class="step-icon">○</span>
|
||
<span>Gotowe</span>
|
||
</div>
|
||
</div>
|
||
<div id="auditResult" style="margin-top: var(--spacing-md); display: none;"></div>
|
||
</div>
|
||
<style>
|
||
.audit-step { display: flex; align-items: center; gap: var(--spacing-sm); padding: 6px 0; font-size: var(--font-size-sm); color: var(--text-secondary); }
|
||
.audit-step.active { color: var(--text-primary); font-weight: 600; }
|
||
.audit-step.active .step-icon { color: var(--primary); }
|
||
.audit-step.done { color: var(--success); }
|
||
.audit-step.done .step-icon { color: var(--success); }
|
||
.audit-step.error { color: var(--danger); }
|
||
.audit-step.error .step-icon { color: var(--danger); }
|
||
.step-icon { font-size: 16px; width: 20px; text-align: center; }
|
||
@keyframes spin { to { transform: rotate(360deg); } }
|
||
</style>
|
||
|
||
<!-- Modal potwierdzenia -->
|
||
<div class="modal" id="confirmModal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<div class="modal-icon warning">
|
||
<svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
||
</svg>
|
||
</div>
|
||
<div class="modal-title">Uruchom audyt SEO</div>
|
||
</div>
|
||
<div class="modal-body">
|
||
Czy na pewno chcesz uruchomic audyt SEO dla {{ company.name }}? Jesli firma juz poprawila strone, wyniki zostana nadpisane nowymi danymi.
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-outline" onclick="closeModal()">Anuluj</button>
|
||
<button class="btn btn-primary" onclick="confirmAudit()">Uruchom audyt</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal informacyjny -->
|
||
<div class="modal" id="infoModal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<div class="modal-icon info" id="infoModalIcon">
|
||
<svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||
</svg>
|
||
</div>
|
||
<div class="modal-title" id="infoModalTitle">Informacja</div>
|
||
</div>
|
||
<div class="modal-body" id="infoModalBody"></div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-primary" onclick="closeInfoModal()">OK</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<style>
|
||
.modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; align-items: center; justify-content: center; }
|
||
.modal.active { display: flex; }
|
||
.modal-content { background: var(--surface); padding: var(--spacing-xl); border-radius: var(--radius-lg, 12px); max-width: 480px; width: 90%; box-shadow: 0 20px 60px rgba(0,0,0,0.3); animation: modalSlideIn 0.2s ease-out; }
|
||
@keyframes modalSlideIn { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } }
|
||
.modal-header { display: flex; align-items: center; gap: var(--spacing-md); margin-bottom: var(--spacing-md); }
|
||
.modal-icon { width: 48px; height: 48px; border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||
.modal-icon.warning { background: #fef3c7; color: #d97706; }
|
||
.modal-icon.success { background: #dcfce7; color: #16a34a; }
|
||
.modal-icon.info { background: #dbeafe; color: #2563eb; }
|
||
.modal-title { font-size: var(--font-size-lg, 18px); font-weight: 600; }
|
||
.modal-body { color: var(--text-secondary); line-height: 1.6; margin-bottom: var(--spacing-lg); }
|
||
.modal-footer { display: flex; justify-content: flex-end; gap: var(--spacing-sm); }
|
||
</style>
|
||
|
||
{% if analysis and analysis.seo_audited_at %}
|
||
<div class="audit-info">
|
||
<span>Ostatni audyt: <strong>{{ analysis.seo_audited_at.strftime('%d.%m.%Y %H:%M') }}</strong></span>
|
||
{% if analysis.cms_detected %}<span>CMS: <strong>{{ analysis.cms_detected }}</strong></span>{% endif %}
|
||
{% if analysis.hosting_provider %}<span>Hosting: <strong>{{ analysis.hosting_provider }}</strong></span>{% endif %}
|
||
{% if analysis.load_time_ms %}<span>Czas ladowania: <strong>{{ analysis.load_time_ms }}ms</strong></span>{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- 4 Score Cards -->
|
||
{% macro score_class(val) %}{% if val is none %}na{% elif val >= 90 %}good{% elif val >= 50 %}medium{% else %}poor{% endif %}{% endmacro %}
|
||
|
||
<div class="scores-grid">
|
||
<div class="score-card">
|
||
<div class="score-ring {{ score_class(analysis.pagespeed_seo_score if analysis else none) }}">
|
||
{{ analysis.pagespeed_seo_score if analysis and analysis.pagespeed_seo_score is not none else '-' }}
|
||
</div>
|
||
<div class="score-name">SEO</div>
|
||
<div class="score-desc">Optymalizacja pod wyszukiwarki: meta tagi, struktura, indeksowalnosc</div>
|
||
</div>
|
||
<div class="score-card">
|
||
<div class="score-ring {{ score_class(analysis.pagespeed_performance_score if analysis else none) }}">
|
||
{{ analysis.pagespeed_performance_score if analysis and analysis.pagespeed_performance_score is not none else '-' }}
|
||
</div>
|
||
<div class="score-name">Performance</div>
|
||
<div class="score-desc">Szybkosc ladowania strony, czas do interakcji, plynnosc dzialania</div>
|
||
</div>
|
||
<div class="score-card">
|
||
<div class="score-ring {{ score_class(analysis.pagespeed_accessibility_score if analysis else none) }}">
|
||
{{ analysis.pagespeed_accessibility_score if analysis and analysis.pagespeed_accessibility_score is not none else '-' }}
|
||
</div>
|
||
<div class="score-name">Accessibility</div>
|
||
<div class="score-desc">Dostepnosc dla osob z niepelnosprawnosciami: kontrast, opisy, nawigacja klawiatura</div>
|
||
</div>
|
||
<div class="score-card">
|
||
<div class="score-ring {{ score_class(analysis.pagespeed_best_practices_score if analysis else none) }}">
|
||
{{ analysis.pagespeed_best_practices_score if analysis and analysis.pagespeed_best_practices_score is not none else '-' }}
|
||
</div>
|
||
<div class="score-name">Best Practices</div>
|
||
<div class="score-desc">Bezpieczenstwo (HTTPS), nowoczesne standardy, brak bledow technicznych</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Zalecenia -->
|
||
<div class="section">
|
||
<h2>Zalecenia — co poprawic</h2>
|
||
{% if recommendations %}
|
||
<ul class="reco-list">
|
||
{% for r in recommendations %}
|
||
<li class="reco-item {{ r.severity }}">
|
||
<span class="reco-severity {{ r.severity }}">
|
||
{% if r.severity == 'critical' %}KRYTYCZNE{% elif r.severity == 'warning' %}UWAGA{% elif r.severity == 'success' %}OK{% else %}INFO{% endif %}
|
||
</span>
|
||
{{ r.text }}
|
||
</li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% else %}
|
||
<p class="text-muted">Brak danych do analizy. Uruchom audyt, zeby zobaczyc zalecenia.</p>
|
||
{% endif %}
|
||
</div>
|
||
|
||
{% if analysis %}
|
||
<div class="two-col">
|
||
<!-- SEO & Tresc -->
|
||
<div class="section">
|
||
<h2>SEO i tresc strony</h2>
|
||
<div class="detail-grid">
|
||
<div class="detail-item">
|
||
<span class="detail-label">Tytul strony (meta title)</span>
|
||
<span class="detail-value {% if analysis.meta_title or analysis.seo_title %}yes{% else %}no{% endif %}">
|
||
{% if analysis.meta_title or analysis.seo_title %}Jest{% else %}Brak{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Opis strony (meta description)</span>
|
||
<span class="detail-value {% if analysis.meta_description or analysis.seo_description %}yes{% else %}no{% endif %}">
|
||
{% if analysis.meta_description or analysis.seo_description %}Jest{% else %}Brak{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Sitemap.xml</span>
|
||
<span class="detail-value {% if analysis.has_sitemap %}yes{% else %}no{% endif %}">
|
||
{% if analysis.has_sitemap %}Jest{% else %}Brak{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Robots.txt</span>
|
||
<span class="detail-value {% if analysis.has_robots_txt %}yes{% else %}no{% endif %}">
|
||
{% if analysis.has_robots_txt %}Jest{% else %}Brak{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Dane strukturalne (Schema.org)</span>
|
||
<span class="detail-value {% if analysis.has_structured_data %}yes{% else %}no{% endif %}">
|
||
{% if analysis.has_structured_data %}Jest{% else %}Brak{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Canonical URL</span>
|
||
<span class="detail-value {% if analysis.has_canonical %}yes{% else %}no{% endif %}">
|
||
{% if analysis.has_canonical %}Jest{% else %}Brak{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Open Graph (Facebook)</span>
|
||
<span class="detail-value {% if analysis.has_og_tags %}yes{% else %}no{% endif %}">
|
||
{% if analysis.has_og_tags %}Jest{% else %}Brak{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Twitter Cards</span>
|
||
<span class="detail-value {% if analysis.has_twitter_cards %}yes{% else %}no{% endif %}">
|
||
{% if analysis.has_twitter_cards %}Jest{% else %}Brak{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Naglowki H1</span>
|
||
<span class="detail-value {% if analysis.h1_count == 1 %}yes{% elif analysis.h1_count is not none %}no{% endif %}">
|
||
{{ analysis.h1_count if analysis.h1_count is not none else '-' }}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Naglowki H2</span>
|
||
<span class="detail-value">{{ analysis.h2_count if analysis.h2_count is not none else '-' }}</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Naglowki H3</span>
|
||
<span class="detail-value">{{ analysis.h3_count if analysis.h3_count is not none else '-' }}</span>
|
||
</div>
|
||
{% if analysis.has_hreflang is not none %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Hreflang — wersje jezykowe</span>
|
||
<span class="detail-value {% if analysis.has_hreflang %}yes{% else %}no{% endif %}">
|
||
{% if analysis.has_hreflang %}Jest{% else %}Brak{% endif %}
|
||
</span>
|
||
</div>
|
||
{% endif %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Obrazki ogolnie</span>
|
||
<span class="detail-value">{{ analysis.total_images if analysis.total_images is not none else '-' }}</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Obrazki bez opisu (alt)</span>
|
||
<span class="detail-value {% if analysis.images_without_alt and analysis.images_without_alt > 0 %}no{% else %}yes{% endif %}">
|
||
{{ analysis.images_without_alt if analysis.images_without_alt is not none else '-' }}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Linki wewnetrzne</span>
|
||
<span class="detail-value">{{ analysis.internal_links_count if analysis.internal_links_count is not none else '-' }}</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Linki zewnetrzne</span>
|
||
<span class="detail-value">{{ analysis.external_links_count if analysis.external_links_count is not none else '-' }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
{% if analysis.meta_title or analysis.seo_title %}
|
||
<div style="margin-top: var(--spacing-lg); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: 4px;">Tytul strony:</div>
|
||
<div style="font-size: var(--font-size-sm); color: var(--text-primary);">{{ analysis.meta_title or analysis.seo_title }}</div>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.meta_description or analysis.seo_description %}
|
||
<div style="margin-top: var(--spacing-sm); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: 4px;">Opis strony:</div>
|
||
<div style="font-size: var(--font-size-sm); color: var(--text-primary);">{{ analysis.meta_description or analysis.seo_description }}</div>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.meta_keywords %}
|
||
<div style="margin-top: var(--spacing-sm); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: 4px;">Meta keywords (slowa kluczowe w kodzie strony):</div>
|
||
<div style="font-size: var(--font-size-sm); color: var(--text-secondary);">{{ analysis.meta_keywords }}</div>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.canonical_url %}
|
||
<div style="margin-top: var(--spacing-sm); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: 4px;">Canonical URL — adres kanoniczny strony:</div>
|
||
<div style="font-size: var(--font-size-sm); color: var(--text-primary); word-break: break-all;">{{ analysis.canonical_url }}</div>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.noindex_reason %}
|
||
<div style="margin-top: var(--spacing-sm); padding: var(--spacing-md); background: #fef2f2; border-radius: var(--radius); border-left: 3px solid var(--danger);">
|
||
<div class="detail-label" style="margin-bottom: 4px; color: var(--danger);">Powod blokady indeksowania:</div>
|
||
<div style="font-size: var(--font-size-sm); color: var(--text-primary);">{{ analysis.noindex_reason }}</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<!-- Podglad Open Graph — jak strona wyglada na Facebooku -->
|
||
{% if analysis.og_title or analysis.og_description or analysis.og_image %}
|
||
<div class="section">
|
||
<h2>Open Graph — podglad udostepnienia na Facebooku</h2>
|
||
<p style="font-size: var(--font-size-xs); color: var(--text-secondary); margin-bottom: var(--spacing-md);">
|
||
Tak wyglada link do tej strony udostepniony na Facebooku, LinkedIn czy Messengerze.
|
||
</p>
|
||
<div style="max-width: 500px; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: #f0f2f5;">
|
||
{% if analysis.og_image %}
|
||
<div style="width: 100%; height: 260px; background: #e4e6eb; display: flex; align-items: center; justify-content: center; overflow: hidden;">
|
||
<img src="{{ analysis.og_image }}" alt="OG Image" referrerpolicy="no-referrer" style="width: 100%; height: 100%; object-fit: cover;" onerror="this.parentElement.innerHTML='<div style=\'text-align:center; padding: 20px;\'><div style=\'color: var(--text-secondary); font-size: var(--font-size-sm); margin-bottom: 8px;\'>Obrazek niedostepny z poziomu naszego serwera</div><a href=\'{{ analysis.og_image }}\' target=\'_blank\' rel=\'noopener\' style=\'color: var(--primary); font-size: 11px; word-break: break-all;\'>{{ analysis.og_image }}</a></div>'">
|
||
</div>
|
||
{% else %}
|
||
<div style="width: 100%; height: 80px; background: #e4e6eb; display: flex; align-items: center; justify-content: center;">
|
||
<span style="color: var(--text-secondary); font-size: var(--font-size-sm);">Brak obrazka Open Graph</span>
|
||
</div>
|
||
{% endif %}
|
||
<div style="padding: var(--spacing-md);">
|
||
<div style="font-size: 11px; color: #65676b; text-transform: uppercase;">{{ analysis.final_url or analysis.website_url or '' }}</div>
|
||
<div style="font-weight: 600; font-size: var(--font-size-base); color: #050505; margin-top: 4px;">{{ analysis.og_title or analysis.meta_title or analysis.seo_title or 'Brak tytulu' }}</div>
|
||
{% if analysis.og_description %}
|
||
<div style="font-size: var(--font-size-sm); color: #65676b; margin-top: 4px; line-height: 1.3;">{{ analysis.og_description[:150] }}{% if analysis.og_description|length > 150 %}...{% endif %}</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Techniczne -->
|
||
<div class="section">
|
||
<h2>Dane techniczne</h2>
|
||
<div class="detail-grid">
|
||
<div class="detail-item">
|
||
<span class="detail-label">SSL (HTTPS)</span>
|
||
<span class="detail-value {% if analysis.has_ssl %}yes{% else %}no{% endif %}">
|
||
{% if analysis.has_ssl %}Tak{% else %}Nie{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Responsywnosc (mobile)</span>
|
||
<span class="detail-value {% if analysis.is_responsive or analysis.is_mobile_friendly %}yes{% else %}no{% endif %}">
|
||
{% if analysis.is_responsive or analysis.is_mobile_friendly %}Tak{% else %}Nie{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Indeksowalnosc</span>
|
||
<span class="detail-value {% if analysis.is_indexable %}yes{% else %}no{% endif %}">
|
||
{% if analysis.is_indexable %}Tak{% else %}Nie{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Viewport (meta tag)</span>
|
||
<span class="detail-value {% if analysis.viewport_configured or analysis.has_viewport_meta %}yes{% else %}no{% endif %}">
|
||
{% if analysis.viewport_configured or analysis.has_viewport_meta %}Tak{% else %}Nie{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Jezyk strony (html lang)</span>
|
||
<span class="detail-value">{{ analysis.html_lang or '-' }}</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">CMS / System</span>
|
||
<span class="detail-value">{{ analysis.cms_detected or '-' }}</span>
|
||
</div>
|
||
{% if analysis.largest_contentful_paint_ms %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">LCP — czas ladowania glownej tresci</span>
|
||
<span class="detail-value {% if analysis.largest_contentful_paint_ms <= 2500 %}yes{% elif analysis.largest_contentful_paint_ms <= 4000 %}{% else %}no{% endif %}">
|
||
{{ (analysis.largest_contentful_paint_ms / 1000)|round(1) }}s
|
||
</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.cumulative_layout_shift is not none %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">CLS — stabilnosc wizualna strony</span>
|
||
<span class="detail-value {% if analysis.cumulative_layout_shift|float <= 0.1 %}yes{% elif analysis.cumulative_layout_shift|float <= 0.25 %}{% else %}no{% endif %}">
|
||
{{ analysis.cumulative_layout_shift }}
|
||
</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.interaction_to_next_paint_ms %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">INP — szybkosc reakcji na klikniecia</span>
|
||
<span class="detail-value {% if analysis.interaction_to_next_paint_ms <= 200 %}yes{% elif analysis.interaction_to_next_paint_ms <= 500 %}{% else %}no{% endif %}">
|
||
{{ analysis.interaction_to_next_paint_ms }}ms
|
||
</span>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
{% if analysis.security_headers_count is not none %}
|
||
<h3 style="margin-top: var(--spacing-lg); font-size: var(--font-size-base); color: var(--text-secondary);">Zabezpieczenia</h3>
|
||
<div class="detail-grid" style="margin-top: var(--spacing-sm);">
|
||
<div class="detail-item">
|
||
<span class="detail-label">HSTS — wymuszenie HTTPS</span>
|
||
<span class="detail-value {% if analysis.has_hsts %}yes{% else %}no{% endif %}">
|
||
{% if analysis.has_hsts %}Tak{% else %}Nie{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">CSP — ochrona przed wstrzykiwaniem skryptow</span>
|
||
<span class="detail-value {% if analysis.has_csp %}yes{% else %}no{% endif %}">
|
||
{% if analysis.has_csp %}Tak{% else %}Nie{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">X-Frame-Options — ochrona przed osadzaniem</span>
|
||
<span class="detail-value {% if analysis.has_x_frame_options %}yes{% else %}no{% endif %}">
|
||
{% if analysis.has_x_frame_options %}Tak{% else %}Nie{% endif %}
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">X-Content-Type — ochrona typu plikow</span>
|
||
<span class="detail-value {% if analysis.has_x_content_type_options %}yes{% else %}no{% endif %}">
|
||
{% if analysis.has_x_content_type_options %}Tak{% else %}Nie{% endif %}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tresc i slowa kluczowe -->
|
||
{% if analysis.content_summary or analysis.services_extracted or analysis.main_keywords or analysis.word_count_homepage %}
|
||
<div class="section">
|
||
<h2>Tresc strony i slowa kluczowe</h2>
|
||
<div class="detail-grid">
|
||
{% if analysis.word_count_homepage %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Liczba slow na stronie glownej</span>
|
||
<span class="detail-value">{{ analysis.word_count_homepage }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.content_richness_score %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Bogactwo tresci</span>
|
||
<span class="detail-value {% if analysis.content_richness_score >= 7 %}yes{% elif analysis.content_richness_score >= 4 %}{% else %}no{% endif %}">{{ analysis.content_richness_score }}/10</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.page_count_estimate %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Szacowana liczba podstron</span>
|
||
<span class="detail-value">{{ analysis.page_count_estimate }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.google_indexed_pages %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Stron zaindeksowanych w Google</span>
|
||
<span class="detail-value">{{ analysis.google_indexed_pages }}</span>
|
||
</div>
|
||
{% endif %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Blog firmowy</span>
|
||
<span class="detail-value {% if analysis.has_blog %}yes{% else %}no{% endif %}">{% if analysis.has_blog %}Jest{% else %}Brak{% endif %}</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Formularz kontaktowy</span>
|
||
<span class="detail-value {% if analysis.has_contact_form %}yes{% else %}no{% endif %}">{% if analysis.has_contact_form %}Jest{% else %}Brak{% endif %}</span>
|
||
</div>
|
||
{% if analysis.has_portfolio is not none %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Portfolio / Realizacje</span>
|
||
<span class="detail-value {% if analysis.has_portfolio %}yes{% else %}no{% endif %}">{% if analysis.has_portfolio %}Jest{% else %}Brak{% endif %}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.has_live_chat is not none %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Live chat na stronie</span>
|
||
<span class="detail-value {% if analysis.has_live_chat %}yes{% else %}no{% endif %}">{% if analysis.has_live_chat %}Jest{% else %}Brak{% endif %}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.broken_links_count is not none %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Zlamane linki</span>
|
||
<span class="detail-value {% if analysis.broken_links_count == 0 %}yes{% else %}no{% endif %}">{{ analysis.broken_links_count }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.modern_image_ratio is not none %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Nowoczesne formaty obrazkow (WebP/AVIF)</span>
|
||
<span class="detail-value {% if analysis.modern_image_ratio|float >= 50 %}yes{% else %}no{% endif %}">{{ analysis.modern_image_ratio|round(0)|int }}%</span>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
{% if analysis.h1_text %}
|
||
<div style="margin-top: var(--spacing-lg); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: 4px;">Naglowek H1 strony:</div>
|
||
<div style="font-size: var(--font-size-sm); color: var(--text-primary); font-weight: 600;">{{ analysis.h1_text }}</div>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.content_summary %}
|
||
<div style="margin-top: var(--spacing-sm); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: 4px;">Podsumowanie tresci (AI):</div>
|
||
<div style="font-size: var(--font-size-sm); color: var(--text-primary); line-height: 1.5;">{{ analysis.content_summary }}</div>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.services_extracted %}
|
||
<div style="margin-top: var(--spacing-sm); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: 4px;">Wykryte uslugi:</div>
|
||
<div style="display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px;">
|
||
{% for service in analysis.services_extracted %}
|
||
<span style="padding: 2px 8px; background: #dbeafe; color: #1e40af; border-radius: var(--radius-sm); font-size: 11px;">{{ service }}</span>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.main_keywords %}
|
||
<div style="margin-top: var(--spacing-sm); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: 4px;">Glowne slowa kluczowe:</div>
|
||
<div style="display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px;">
|
||
{% for kw in analysis.main_keywords %}
|
||
<span style="padding: 2px 8px; background: #f0fdf4; color: #166534; border-radius: var(--radius-sm); font-size: 11px;">{{ kw }}</span>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.structured_data_types %}
|
||
<div style="margin-top: var(--spacing-sm); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: 4px;">Typy danych strukturalnych (Schema.org):</div>
|
||
<div style="display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px;">
|
||
{% for sdt in analysis.structured_data_types %}
|
||
<span style="padding: 2px 8px; background: #fef3c7; color: #92400e; border-radius: var(--radius-sm); font-size: 11px;">{{ sdt }}</span>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- CrUX — dane od prawdziwych uzytkownikow -->
|
||
{% if analysis.crux_lcp_ms or analysis.crux_inp_ms or analysis.crux_cls is not none %}
|
||
<div class="section">
|
||
<h2>Core Web Vitals — dane od prawdziwych uzytkownikow (CrUX)</h2>
|
||
<p style="font-size: var(--font-size-xs); color: var(--text-secondary); margin-bottom: var(--spacing-md);">
|
||
Te metryki pochodza z przegladarek Chrome prawdziwych odwiedzajacych, nie z testow laboratoryjnych. Wyniki moga sie roznic od Lighthouse.
|
||
</p>
|
||
<div class="detail-grid">
|
||
{% if analysis.crux_lcp_ms %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">LCP — czas glownej tresci (p75)</span>
|
||
<span class="detail-value {% if analysis.crux_lcp_ms <= 2500 %}yes{% elif analysis.crux_lcp_ms <= 4000 %}{% else %}no{% endif %}">{{ (analysis.crux_lcp_ms / 1000)|round(1) }}s</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.crux_fcp_ms %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">FCP — czas pierwszej tresci (p75)</span>
|
||
<span class="detail-value {% if analysis.crux_fcp_ms <= 1800 %}yes{% elif analysis.crux_fcp_ms <= 3000 %}{% else %}no{% endif %}">{{ (analysis.crux_fcp_ms / 1000)|round(1) }}s</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.crux_inp_ms %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">INP — szybkosc reakcji (p75)</span>
|
||
<span class="detail-value {% if analysis.crux_inp_ms <= 200 %}yes{% elif analysis.crux_inp_ms <= 500 %}{% else %}no{% endif %}">{{ analysis.crux_inp_ms }}ms</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.crux_cls is not none %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">CLS — stabilnosc wizualna (p75)</span>
|
||
<span class="detail-value {% if analysis.crux_cls|float <= 0.1 %}yes{% elif analysis.crux_cls|float <= 0.25 %}{% else %}no{% endif %}">{{ analysis.crux_cls }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.crux_ttfb_ms %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">TTFB — czas odpowiedzi serwera (p75)</span>
|
||
<span class="detail-value {% if analysis.crux_ttfb_ms <= 800 %}yes{% elif analysis.crux_ttfb_ms <= 1800 %}{% else %}no{% endif %}">{{ analysis.crux_ttfb_ms }}ms</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.crux_lcp_good_pct is not none %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Uzytkownicy z dobrym LCP</span>
|
||
<span class="detail-value {% if analysis.crux_lcp_good_pct|float >= 75 %}yes{% elif analysis.crux_lcp_good_pct|float >= 50 %}{% else %}no{% endif %}">{{ analysis.crux_lcp_good_pct }}%</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.crux_inp_good_pct is not none %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Uzytkownicy z dobrym INP</span>
|
||
<span class="detail-value {% if analysis.crux_inp_good_pct|float >= 75 %}yes{% elif analysis.crux_inp_good_pct|float >= 50 %}{% else %}no{% endif %}">{{ analysis.crux_inp_good_pct }}%</span>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Google Search Console -->
|
||
{% if analysis.gsc_clicks or analysis.gsc_impressions %}
|
||
<div class="section">
|
||
<h2>Google Search Console — widocznosc w wyszukiwarce</h2>
|
||
<p style="font-size: var(--font-size-xs); color: var(--text-secondary); margin-bottom: var(--spacing-md);">
|
||
Dane z ostatnich {{ analysis.gsc_period_days or 28 }} dni. Pokazuja, jak strona wypada w wynikach wyszukiwania Google.
|
||
</p>
|
||
<div class="detail-grid">
|
||
<div class="detail-item">
|
||
<span class="detail-label">Klikniecia z Google</span>
|
||
<span class="detail-value">{{ analysis.gsc_clicks or 0 }}</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Wyswietlenia w Google</span>
|
||
<span class="detail-value">{{ analysis.gsc_impressions or 0 }}</span>
|
||
</div>
|
||
{% if analysis.gsc_ctr %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">CTR — procent klikniec</span>
|
||
<span class="detail-value">{{ analysis.gsc_ctr }}%</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.gsc_avg_position %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Srednia pozycja w Google</span>
|
||
<span class="detail-value {% if analysis.gsc_avg_position|float <= 10 %}yes{% elif analysis.gsc_avg_position|float <= 30 %}{% else %}no{% endif %}">{{ analysis.gsc_avg_position }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.gsc_index_status %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Status indeksowania</span>
|
||
<span class="detail-value {% if analysis.gsc_index_status == 'PASS' %}yes{% else %}no{% endif %}">{{ analysis.gsc_index_status }}</span>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% if analysis.gsc_top_queries and analysis.gsc_top_queries is not mapping %}
|
||
<div style="margin-top: var(--spacing-lg); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: var(--spacing-sm);">Top zapytania, po ktorych ludzie trafiaja na strone:</div>
|
||
<table style="width: 100%; font-size: var(--font-size-sm); border-collapse: collapse;">
|
||
<thead>
|
||
<tr style="border-bottom: 1px solid var(--border);">
|
||
<th style="text-align: left; padding: 4px 8px; color: var(--text-secondary);">Zapytanie</th>
|
||
<th style="text-align: right; padding: 4px 8px; color: var(--text-secondary);">Klikniecia</th>
|
||
<th style="text-align: right; padding: 4px 8px; color: var(--text-secondary);">Wyswietlenia</th>
|
||
<th style="text-align: right; padding: 4px 8px; color: var(--text-secondary);">Pozycja</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for q in analysis.gsc_top_queries[:10] %}
|
||
<tr style="border-bottom: 1px solid var(--border);">
|
||
<td style="padding: 4px 8px;">{{ q.query if q.query is defined else q.get('keys', [''])[0] }}</td>
|
||
<td style="text-align: right; padding: 4px 8px; font-weight: 600;">{{ q.clicks if q.clicks is defined else q.get('clicks', 0) }}</td>
|
||
<td style="text-align: right; padding: 4px 8px;">{{ q.impressions if q.impressions is defined else q.get('impressions', 0) }}</td>
|
||
<td style="text-align: right; padding: 4px 8px;">{{ "%.1f"|format(q.position if q.position is defined else q.get('position', 0)) }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.gsc_top_pages and analysis.gsc_top_pages is not mapping %}
|
||
<div style="margin-top: var(--spacing-sm); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: var(--spacing-sm);">Najpopularniejsze podstrony w Google:</div>
|
||
<table style="width: 100%; font-size: var(--font-size-sm); border-collapse: collapse;">
|
||
<thead>
|
||
<tr style="border-bottom: 1px solid var(--border);">
|
||
<th style="text-align: left; padding: 4px 8px; color: var(--text-secondary);">Strona</th>
|
||
<th style="text-align: right; padding: 4px 8px; color: var(--text-secondary);">Klikniecia</th>
|
||
<th style="text-align: right; padding: 4px 8px; color: var(--text-secondary);">Wyswietlenia</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for p in analysis.gsc_top_pages[:10] %}
|
||
<tr style="border-bottom: 1px solid var(--border);">
|
||
<td style="padding: 4px 8px; word-break: break-all; max-width: 300px;">{{ p.get('page', p.get('keys', [''])[0]) }}</td>
|
||
<td style="text-align: right; padding: 4px 8px; font-weight: 600;">{{ p.get('clicks', 0) }}</td>
|
||
<td style="text-align: right; padding: 4px 8px;">{{ p.get('impressions', 0) }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Google Business Profile -->
|
||
{% if analysis.google_rating or analysis.google_place_id %}
|
||
<div class="section">
|
||
<h2>Google Business Profile</h2>
|
||
<div class="detail-grid">
|
||
{% if analysis.google_rating %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Ocena Google</span>
|
||
<span class="detail-value">{{ analysis.google_rating }}/5 ({{ analysis.google_reviews_count or 0 }} opinii)</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.google_name %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Nazwa w Google</span>
|
||
<span class="detail-value">{{ analysis.google_name }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.google_address %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Adres w Google</span>
|
||
<span class="detail-value">{{ analysis.google_address }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.google_phone %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Telefon z Google</span>
|
||
<span class="detail-value">{{ analysis.google_phone }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.google_website %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Strona WWW w Google</span>
|
||
<span class="detail-value"><a href="{{ analysis.google_website }}" target="_blank" rel="noopener" style="color: var(--primary); font-size: 11px; word-break: break-all;">{{ analysis.google_website }}</a></span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.google_business_status %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Status</span>
|
||
<span class="detail-value {% if analysis.google_business_status == 'OPERATIONAL' %}yes{% endif %}">{{ analysis.google_business_status }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.google_types %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Kategorie w Google</span>
|
||
<span class="detail-value" style="font-size: 11px;">{{ analysis.google_types|join(', ') }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.google_photos_count %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Zdjecia w Google</span>
|
||
<span class="detail-value">{{ analysis.google_photos_count }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.google_posts_count %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Posty w Google (Google Posts)</span>
|
||
<span class="detail-value">{{ analysis.google_posts_count }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.google_review_response_rate is not none %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Odpowiedzi na opinie</span>
|
||
<span class="detail-value {% if analysis.google_review_response_rate|float >= 80 %}yes{% elif analysis.google_review_response_rate|float >= 50 %}{% else %}no{% endif %}">{{ analysis.google_review_response_rate }}%</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.google_maps_url %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Link do Google Maps</span>
|
||
<span class="detail-value"><a href="{{ analysis.google_maps_url }}" target="_blank" rel="noopener" style="color: var(--primary);">Otworz</a></span>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% if analysis.google_editorial_summary %}
|
||
<div style="margin-top: var(--spacing-lg); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: 4px;">Opis firmy w Google:</div>
|
||
<div style="font-size: var(--font-size-sm); color: var(--text-primary); line-height: 1.5;">{{ analysis.google_editorial_summary }}</div>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.google_opening_hours %}
|
||
<div style="margin-top: var(--spacing-sm); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: var(--spacing-sm);">Godziny otwarcia z Google:</div>
|
||
{% set hours_data = analysis.google_opening_hours %}
|
||
{% if hours_data is mapping and hours_data.get('weekday_text') %}
|
||
{% for entry in hours_data.weekday_text %}
|
||
<div style="font-size: var(--font-size-sm); padding: 3px 0; border-bottom: 1px solid var(--border);">{{ entry }}</div>
|
||
{% endfor %}
|
||
{% elif hours_data is mapping and hours_data.get('periods') %}
|
||
{% set day_names = {0: 'Niedziela', 1: 'Poniedzialek', 2: 'Wtorek', 3: 'Sroda', 4: 'Czwartek', 5: 'Piatek', 6: 'Sobota'} %}
|
||
{% for period in hours_data.periods %}
|
||
<div style="font-size: var(--font-size-sm); padding: 3px 0; border-bottom: 1px solid var(--border);">
|
||
{{ day_names.get(period.open.day, period.open.day) }}: {{ period.open.time[:2] }}:{{ period.open.time[2:] }}–{{ period.close.time[:2] }}:{{ period.close.time[2:] }}
|
||
</div>
|
||
{% endfor %}
|
||
{% elif hours_data is iterable and hours_data is not string and hours_data is not mapping %}
|
||
{% for entry in hours_data %}
|
||
<div style="font-size: var(--font-size-sm); padding: 3px 0; border-bottom: 1px solid var(--border);">{{ entry }}</div>
|
||
{% endfor %}
|
||
{% else %}
|
||
<div style="font-size: var(--font-size-sm);">{{ hours_data }}</div>
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.google_reviews_data and analysis.google_reviews_data is iterable and analysis.google_reviews_data is not mapping %}
|
||
<div style="margin-top: var(--spacing-sm); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: var(--spacing-sm);">Ostatnie opinie Google:</div>
|
||
{% for review in analysis.google_reviews_data[:5] %}
|
||
<div style="padding: var(--spacing-sm) 0; border-bottom: 1px solid var(--border); font-size: var(--font-size-sm);">
|
||
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
|
||
<span style="font-weight: 600;">{{ review.get('author', review.get('authorAttribution', {}).get('displayName', 'Anonim')) }}</span>
|
||
<span style="color: var(--warning);">
|
||
{% set stars = review.get('rating', review.get('star_rating', 0))|int %}
|
||
{% for i in range(stars) %}★{% endfor %}{% for i in range(5 - stars) %}☆{% endfor %}
|
||
</span>
|
||
</div>
|
||
{% set review_text = review.get('text', review.get('comment', review.get('originalText', {}).get('text', ''))) %}
|
||
{% if review_text %}
|
||
<div style="color: var(--text-secondary); line-height: 1.4;">{{ review_text[:200] }}{% if review_text|length > 200 %}...{% endif %}</div>
|
||
{% endif %}
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- GBP statystyki -->
|
||
{% if analysis.gbp_impressions_maps or analysis.gbp_impressions_search or analysis.gbp_call_clicks %}
|
||
<div class="section">
|
||
<h2>Statystyki Google Business Profile</h2>
|
||
<p style="font-size: var(--font-size-xs); color: var(--text-secondary); margin-bottom: var(--spacing-md);">
|
||
Dane z ostatnich {{ analysis.gbp_performance_period_days or 30 }} dni.
|
||
</p>
|
||
<div class="detail-grid">
|
||
{% if analysis.gbp_impressions_search %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Wyswietlenia w wyszukiwarce Google</span>
|
||
<span class="detail-value">{{ analysis.gbp_impressions_search }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.gbp_impressions_maps %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Wyswietlenia w Google Maps</span>
|
||
<span class="detail-value">{{ analysis.gbp_impressions_maps }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.gbp_website_clicks %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Klikniecia w strone WWW</span>
|
||
<span class="detail-value">{{ analysis.gbp_website_clicks }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.gbp_call_clicks %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Klikniecia w telefon</span>
|
||
<span class="detail-value">{{ analysis.gbp_call_clicks }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.gbp_direction_requests %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Zapytania o dojazd</span>
|
||
<span class="detail-value">{{ analysis.gbp_direction_requests }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.gbp_conversations %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Wiadomosci (conversations)</span>
|
||
<span class="detail-value">{{ analysis.gbp_conversations }}</span>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% if analysis.gbp_search_keywords and analysis.gbp_search_keywords is not mapping %}
|
||
<div style="margin-top: var(--spacing-lg); padding: var(--spacing-md); background: var(--background); border-radius: var(--radius);">
|
||
<div class="detail-label" style="margin-bottom: var(--spacing-sm);">Po jakich slowach ludzie szukaja tej firmy w Google Maps:</div>
|
||
<table style="width: 100%; font-size: var(--font-size-sm); border-collapse: collapse;">
|
||
<thead>
|
||
<tr style="border-bottom: 1px solid var(--border);">
|
||
<th style="text-align: left; padding: 4px 8px; color: var(--text-secondary);">Slowo kluczowe</th>
|
||
<th style="text-align: right; padding: 4px 8px; color: var(--text-secondary);">Wyswietlenia</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for kw in analysis.gbp_search_keywords[:15] %}
|
||
<tr style="border-bottom: 1px solid var(--border);">
|
||
<td style="padding: 4px 8px;">{{ kw.get('keyword', kw.get('searchKeyword', kw)) if kw is mapping else kw }}</td>
|
||
<td style="text-align: right; padding: 4px 8px; font-weight: 600;">{{ kw.get('impressions', kw.get('insightsValue', {}).get('value', '-')) if kw is mapping else '' }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Infrastruktura i szczegoly techniczne -->
|
||
{% if analysis.ssl_expires_at or analysis.final_url or analysis.hosting_provider or analysis.server_software or analysis.hosting_ip %}
|
||
<div class="section">
|
||
<h2>Infrastruktura i szczegoly techniczne</h2>
|
||
<div class="detail-grid">
|
||
{% if analysis.final_url and analysis.final_url != analysis.website_url %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">URL po przekierowaniu</span>
|
||
<span class="detail-value" style="font-size: 11px; word-break: break-all;">{{ analysis.final_url }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.http_status_code %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Kod odpowiedzi HTTP</span>
|
||
<span class="detail-value {% if analysis.http_status_code == 200 %}yes{% else %}no{% endif %}">{{ analysis.http_status_code }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.ssl_expires_at %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Certyfikat SSL wygasa</span>
|
||
<span class="detail-value">{{ analysis.ssl_expires_at.strftime('%d.%m.%Y') }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.ssl_issuer %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Wystawca SSL</span>
|
||
<span class="detail-value">{{ analysis.ssl_issuer }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.hosting_provider or ip_info.get('isp') %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Hosting</span>
|
||
<span class="detail-value">{{ analysis.hosting_provider or ip_info.get('isp', '') }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if ip_info.get('org') and ip_info.get('org') != (analysis.hosting_provider or '') %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Firma hostingowa</span>
|
||
<span class="detail-value">{{ ip_info.org }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.hosting_ip %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Adres IP serwera</span>
|
||
<span class="detail-value" style="font-family: monospace;">{{ analysis.hosting_ip }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if ip_info.get('city') %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Lokalizacja serwera</span>
|
||
<span class="detail-value">{{ ip_info.city }}{% if ip_info.get('region') %}, {{ ip_info.region }}{% endif %}{% if ip_info.get('country') %}, {{ ip_info.country }}{% endif %}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if ip_info.get('as_number') %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Siec (AS)</span>
|
||
<span class="detail-value" style="font-size: 11px;">{{ ip_info.as_number }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.server_software %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Oprogramowanie serwera</span>
|
||
<span class="detail-value">{{ analysis.server_software }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.site_author %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Tworca strony</span>
|
||
<span class="detail-value">{{ analysis.site_author }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.site_generator %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Generator strony</span>
|
||
<span class="detail-value">{{ analysis.site_generator }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.domain_registrar %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Rejestrator domeny</span>
|
||
<span class="detail-value">{{ analysis.domain_registrar }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.frameworks_detected %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Wykryte technologie</span>
|
||
<span class="detail-value">{{ analysis.frameworks_detected|join(', ') }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.last_modified_at %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Ostatnia modyfikacja strony</span>
|
||
<span class="detail-value">{{ analysis.last_modified_at.strftime('%d.%m.%Y %H:%M') }}</span>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Diagnostyka audytu -->
|
||
{% if analysis and (analysis.audit_source or analysis.audit_errors or analysis.analyzed_at) %}
|
||
<div class="section" style="opacity: 0.7;">
|
||
<h2>Diagnostyka audytu</h2>
|
||
<p style="font-size: var(--font-size-xs); color: var(--text-secondary); margin-bottom: var(--spacing-md);">
|
||
Informacje techniczne o ostatnim uruchomieniu audytu.
|
||
</p>
|
||
<div class="detail-grid">
|
||
{% if analysis.analyzed_at %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Data audytu</span>
|
||
<span class="detail-value">{{ analysis.analyzed_at.strftime('%d.%m.%Y %H:%M') }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.audit_source %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Zrodlo danych</span>
|
||
<span class="detail-value">{{ analysis.audit_source }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if analysis.audit_version %}
|
||
<div class="detail-item">
|
||
<span class="detail-label">Wersja audytu</span>
|
||
<span class="detail-value">{{ analysis.audit_version }}</span>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% if analysis.audit_errors %}
|
||
<div style="margin-top: var(--spacing-md); padding: var(--spacing-md); background: #fef2f2; border-radius: var(--radius); border-left: 3px solid var(--danger);">
|
||
<div class="detail-label" style="margin-bottom: 4px; color: var(--danger);">Bledy podczas audytu:</div>
|
||
<div style="font-size: var(--font-size-sm); color: var(--text-primary); white-space: pre-wrap;">{{ analysis.audit_errors }}</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
{% endif %}
|
||
{% endblock %}
|
||
|
||
{% block extra_js %}
|
||
var csrfToken = '{{ csrf_token() }}';
|
||
|
||
// Modal functions
|
||
function closeModal() {
|
||
document.getElementById('confirmModal').classList.remove('active');
|
||
}
|
||
function showInfoModal(title, body) {
|
||
document.getElementById('infoModalTitle').textContent = title;
|
||
document.getElementById('infoModalBody').textContent = body;
|
||
document.getElementById('infoModal').classList.add('active');
|
||
}
|
||
function closeInfoModal() {
|
||
document.getElementById('infoModal').classList.remove('active');
|
||
}
|
||
document.getElementById('confirmModal').addEventListener('click', function(e) {
|
||
if (e.target.id === 'confirmModal') closeModal();
|
||
});
|
||
document.getElementById('infoModal').addEventListener('click', function(e) {
|
||
if (e.target.id === 'infoModal') closeInfoModal();
|
||
});
|
||
|
||
// Stepper helpers
|
||
function setStep(stepId, state) {
|
||
var el = document.getElementById(stepId);
|
||
var icon = el.querySelector('.step-icon');
|
||
el.className = 'audit-step ' + state;
|
||
if (state === 'active') icon.textContent = '◉';
|
||
else if (state === 'done') icon.textContent = '✓';
|
||
else if (state === 'error') icon.textContent = '✗';
|
||
else icon.textContent = '○';
|
||
}
|
||
|
||
function runAudit() {
|
||
document.getElementById('confirmModal').classList.add('active');
|
||
}
|
||
|
||
var stepTimers = [];
|
||
var allSteps = ['step-connect', 'step-pagespeed', 'step-onpage', 'step-technical', 'step-gsc', 'step-save', 'step-done'];
|
||
|
||
function clearStepTimers() {
|
||
stepTimers.forEach(function(t) { clearTimeout(t); });
|
||
stepTimers = [];
|
||
}
|
||
|
||
function confirmAudit() {
|
||
closeModal();
|
||
|
||
var btn = document.getElementById('auditBtn');
|
||
var progress = document.getElementById('auditProgress');
|
||
var result = document.getElementById('auditResult');
|
||
|
||
btn.disabled = true;
|
||
btn.textContent = 'Audyt w toku...';
|
||
progress.style.display = 'block';
|
||
progress.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||
result.style.display = 'none';
|
||
|
||
// Reset all steps
|
||
allSteps.forEach(function(id) { setStep(id, ''); });
|
||
clearStepTimers();
|
||
|
||
// Simulate steps progressing while API works
|
||
// API typically takes 10-25 seconds total
|
||
var apiDone = false;
|
||
var currentStepIdx = 0;
|
||
|
||
function advanceStep() {
|
||
if (apiDone || currentStepIdx >= allSteps.length - 1) return; // don't touch 'done' step
|
||
if (currentStepIdx > 0) setStep(allSteps[currentStepIdx - 1], 'done');
|
||
setStep(allSteps[currentStepIdx], 'active');
|
||
currentStepIdx++;
|
||
}
|
||
|
||
// Start step progression
|
||
advanceStep(); // step-connect immediately
|
||
stepTimers.push(setTimeout(function() { advanceStep(); }, 3000)); // step-pagespeed at 3s
|
||
stepTimers.push(setTimeout(function() { advanceStep(); }, 8000)); // step-onpage at 8s
|
||
stepTimers.push(setTimeout(function() { advanceStep(); }, 13000)); // step-technical at 13s
|
||
stepTimers.push(setTimeout(function() { advanceStep(); }, 18000)); // step-gsc at 18s
|
||
stepTimers.push(setTimeout(function() { advanceStep(); }, 23000)); // step-save at 23s
|
||
|
||
fetch('/api/seo/audit', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken },
|
||
body: JSON.stringify({ slug: '{{ company.slug }}' })
|
||
}).then(function(r) {
|
||
return r.json();
|
||
}).then(function(data) {
|
||
apiDone = true;
|
||
clearStepTimers();
|
||
|
||
if (data.success) {
|
||
// Mark all steps as done
|
||
allSteps.forEach(function(id) { setStep(id, 'done'); });
|
||
|
||
// Show scores in result area — stays visible until user hides
|
||
var scores = data.seo_audit ? data.seo_audit.pagespeed : null;
|
||
result.style.display = 'block';
|
||
var html = '<div style="padding: var(--spacing-md); background: #f0fdf4; border-radius: var(--radius); border-left: 3px solid var(--success);">' +
|
||
'<strong style="color: var(--success);">Audyt zakonczony pomyslnie</strong>';
|
||
if (scores) {
|
||
html += '<div style="margin-top: var(--spacing-sm); font-size: var(--font-size-sm); display: flex; gap: var(--spacing-lg); flex-wrap: wrap;">' +
|
||
'<span>SEO: <strong>' + (scores.seo_score || '-') + '</strong></span>' +
|
||
'<span>Performance: <strong>' + (scores.performance_score || '-') + '</strong></span>' +
|
||
'<span>Dostepnosc: <strong>' + (scores.accessibility_score || '-') + '</strong></span>' +
|
||
'<span>Best Practices: <strong>' + (scores.best_practices_score || '-') + '</strong></span>' +
|
||
'</div>';
|
||
}
|
||
html += '<div style="margin-top: var(--spacing-md); display: flex; gap: var(--spacing-sm);">' +
|
||
'<button class="btn btn-primary btn-sm" onclick="location.reload()">Odswiez strone z nowymi danymi</button>' +
|
||
'<button class="btn btn-outline btn-sm" onclick="hideAuditProgress()">Ukryj</button>' +
|
||
'</div></div>';
|
||
result.innerHTML = html;
|
||
|
||
btn.disabled = false;
|
||
btn.textContent = 'Uruchom audyt ponownie';
|
||
} else {
|
||
// Mark failed step
|
||
for (var i = 0; i < allSteps.length; i++) {
|
||
var stepEl = document.getElementById(allSteps[i]);
|
||
if (stepEl.classList.contains('active')) {
|
||
setStep(allSteps[i], 'error');
|
||
break;
|
||
}
|
||
}
|
||
result.style.display = 'block';
|
||
result.innerHTML = '<div style="padding: var(--spacing-md); background: #fef2f2; border-radius: var(--radius); border-left: 3px solid var(--danger);">' +
|
||
'<strong style="color: var(--danger);">Blad audytu</strong>' +
|
||
'<div style="margin-top: var(--spacing-sm); font-size: var(--font-size-sm);">' + (data.error || 'Nieznany blad') + '</div>' +
|
||
'<div style="margin-top: var(--spacing-md);">' +
|
||
'<button class="btn btn-outline btn-sm" onclick="hideAuditProgress()">Ukryj</button>' +
|
||
'</div></div>';
|
||
btn.disabled = false;
|
||
btn.textContent = 'Uruchom audyt ponownie';
|
||
}
|
||
}).catch(function(e) {
|
||
apiDone = true;
|
||
clearStepTimers();
|
||
setStep('step-connect', 'error');
|
||
result.style.display = 'block';
|
||
result.innerHTML = '<div style="padding: var(--spacing-md); background: #fef2f2; border-radius: var(--radius); border-left: 3px solid var(--danger);">' +
|
||
'<strong style="color: var(--danger);">Blad polaczenia</strong>' +
|
||
'<div style="margin-top: var(--spacing-sm); font-size: var(--font-size-sm);">' + e.message + '</div>' +
|
||
'<div style="margin-top: var(--spacing-md);">' +
|
||
'<button class="btn btn-outline btn-sm" onclick="hideAuditProgress()">Ukryj</button>' +
|
||
'</div></div>';
|
||
btn.disabled = false;
|
||
btn.textContent = 'Uruchom audyt ponownie';
|
||
});
|
||
}
|
||
|
||
function hideAuditProgress() {
|
||
document.getElementById('auditProgress').style.display = 'none';
|
||
}
|
||
{% endblock %}
|