{% extends "base.html" %} {% block title %}Audyt SEO: {{ company.name }} - Panel Admina{% endblock %} {% block extra_css %} {% endblock %} {% block content %} Powrot do listy firm {% if analysis and analysis.seo_audited_at %}
Ostatni audyt: {{ analysis.seo_audited_at|local_time('%d.%m.%Y %H:%M') }} {% if analysis.cms_detected %}CMS: {{ analysis.cms_detected }}{% endif %} {% if analysis.hosting_provider %}Hosting: {{ analysis.hosting_provider }}{% endif %} {% if analysis.load_time_ms %}Czas ladowania: {{ analysis.load_time_ms }}ms{% endif %}
{% endif %} {% macro score_class(val) %}{% if val is none %}na{% elif val >= 90 %}good{% elif val >= 50 %}medium{% else %}poor{% endif %}{% endmacro %}
{{ analysis.pagespeed_seo_score if analysis and analysis.pagespeed_seo_score is not none else '-' }}
SEO
Optymalizacja pod wyszukiwarki: meta tagi, struktura, indeksowalnosc
{{ analysis.pagespeed_performance_score if analysis and analysis.pagespeed_performance_score is not none else '-' }}
Performance
Szybkosc ladowania strony, czas do interakcji, plynnosc dzialania
{{ analysis.pagespeed_accessibility_score if analysis and analysis.pagespeed_accessibility_score is not none else '-' }}
Accessibility
Dostepnosc dla osob z niepelnosprawnosciami: kontrast, opisy, nawigacja klawiatura
{{ analysis.pagespeed_best_practices_score if analysis and analysis.pagespeed_best_practices_score is not none else '-' }}
Best Practices
Bezpieczenstwo (HTTPS), nowoczesne standardy, brak bledow technicznych
{% if all_benchmarks %}

Porownanie z innymi firmami Norda Biznes

Srednie wyniki {{ all_benchmarks.count }} zbadanych firm w stowarzyszeniu{% if benchmarks %} oraz {{ benchmarks.count }} firm w kategorii "{{ benchmarks.category_name }}"{% endif %}.

{% if benchmarks %}{% endif %} {% set metrics = [ ('SEO', analysis.pagespeed_seo_score, benchmarks.get('avg_seo'), all_benchmarks.avg_seo), ('Performance', analysis.pagespeed_performance_score, benchmarks.get('avg_performance'), all_benchmarks.avg_performance), ('Dostepnosc', analysis.pagespeed_accessibility_score, benchmarks.get('avg_accessibility'), all_benchmarks.avg_accessibility), ('Best Practices', analysis.pagespeed_best_practices_score, benchmarks.get('avg_best_practices'), all_benchmarks.avg_best_practices), ] %} {% for name, val, cat_avg, all_avg in metrics %} {% if benchmarks %} {% endif %} {% endfor %}
Metryka {{ company.name[:25] }}{{ benchmarks.category_name[:20] }} ({{ benchmarks.count }})Wszystkie firmy ({{ all_benchmarks.count }})
{{ name }} {% if val is not none %} {{ val }} {% if val > all_avg %} {% elif val < all_avg %} {% else %} {% endif %} {% else %}-{% endif %} {{ cat_avg if cat_avg else '-' }}{{ all_avg }}
{% endif %}

Zalecenia — co poprawic

{% if recommendations %} {% else %}

Brak danych do analizy. Uruchom audyt, zeby zobaczyc zalecenia.

{% endif %}
{% if analysis %}

SEO i tresc strony

Tytul strony (meta title) {% if analysis.meta_title or analysis.seo_title %}Jest{% else %}Brak{% endif %}
Opis strony (meta description) {% if analysis.meta_description or analysis.seo_description %}Jest{% else %}Brak{% endif %}
Sitemap.xml {% if analysis.has_sitemap %}Jest{% else %}Brak{% endif %}
Robots.txt {% if analysis.has_robots_txt %}Jest{% else %}Brak{% endif %}
Dane strukturalne (Schema.org) {% if analysis.has_structured_data %}Jest{% else %}Brak{% endif %}
Canonical URL {% if analysis.has_canonical %}Jest{% else %}Brak{% endif %}
Open Graph (Facebook) {% if analysis.has_og_tags %}Jest{% else %}Brak{% endif %}
Twitter Cards {% if analysis.has_twitter_cards %}Jest{% else %}Brak{% endif %}
Naglowki H1 {{ analysis.h1_count if analysis.h1_count is not none else '-' }}
Naglowki H2 {{ analysis.h2_count if analysis.h2_count is not none else '-' }}
Naglowki H3 {{ analysis.h3_count if analysis.h3_count is not none else '-' }}
{% if analysis.has_hreflang is not none %}
Hreflang — wersje jezykowe {% if analysis.has_hreflang %}Jest{% else %}Brak{% endif %}
{% endif %}
Obrazki ogolnie {{ analysis.total_images if analysis.total_images is not none else '-' }}
Obrazki bez opisu (alt) {{ analysis.images_without_alt if analysis.images_without_alt is not none else '-' }}
Linki wewnetrzne {{ analysis.internal_links_count if analysis.internal_links_count is not none else '-' }}
Linki zewnetrzne {{ analysis.external_links_count if analysis.external_links_count is not none else '-' }}
{% if analysis.meta_title or analysis.seo_title %}
Tytul strony:
{{ analysis.meta_title or analysis.seo_title }}
{% endif %} {% if analysis.meta_description or analysis.seo_description %}
Opis strony:
{{ analysis.meta_description or analysis.seo_description }}
{% endif %} {% if analysis.meta_keywords %}
Meta keywords (slowa kluczowe w kodzie strony):
{{ analysis.meta_keywords }}
{% endif %} {% if analysis.canonical_url %}
Canonical URL — adres kanoniczny strony:
{{ analysis.canonical_url }}
{% endif %} {% if analysis.noindex_reason %}
Powod blokady indeksowania:
{{ analysis.noindex_reason }}
{% endif %}
{% if analysis.og_title or analysis.og_description or analysis.og_image %}

Open Graph — podglad udostepnienia na Facebooku

Tak wyglada link do tej strony udostepniony na Facebooku, LinkedIn czy Messengerze.

{% if analysis.og_image %}
OG Image
{% else %}
Brak obrazka Open Graph
{% endif %}
{{ analysis.final_url or analysis.website_url or '' }}
{{ analysis.og_title or analysis.meta_title or analysis.seo_title or 'Brak tytulu' }}
{% if analysis.og_description %}
{{ analysis.og_description[:150] }}{% if analysis.og_description|length > 150 %}...{% endif %}
{% endif %}
{% endif %}

Dane techniczne

SSL (HTTPS) {% if analysis.has_ssl %}Tak{% else %}Nie{% endif %}
Responsywnosc (mobile) {% if analysis.is_responsive or analysis.is_mobile_friendly %}Tak{% else %}Nie{% endif %}
Indeksowalnosc {% if analysis.is_indexable %}Tak{% else %}Nie{% endif %}
Viewport (meta tag) {% if analysis.viewport_configured or analysis.has_viewport_meta %}Tak{% else %}Nie{% endif %}
Jezyk strony (html lang) {{ analysis.html_lang or '-' }}
CMS / System {{ analysis.cms_detected or '-' }}
{% if analysis.largest_contentful_paint_ms %}
LCP — czas ladowania glownej tresci {{ (analysis.largest_contentful_paint_ms / 1000)|round(1) }}s
{% endif %} {% if analysis.cumulative_layout_shift is not none %}
CLS — stabilnosc wizualna strony {{ analysis.cumulative_layout_shift }}
{% endif %} {% if analysis.interaction_to_next_paint_ms %}
INP — szybkosc reakcji na klikniecia {{ analysis.interaction_to_next_paint_ms }}ms
{% endif %}
{% if analysis.pagespeed_audits and analysis.pagespeed_audits is mapping and analysis.pagespeed_audits.get('performance') %} {% set perf_data = analysis.pagespeed_audits.performance %}

Szczegolowe metryki wydajnosci (Lighthouse)

{% if perf_data.get('speed-index') %} {% set si = perf_data['speed-index'] %}
Speed Index — jak szybko tresc staje sie widoczna {{ si.get('displayValue', '-') }}
{% endif %} {% if perf_data.get('interactive') %} {% set tti = perf_data['interactive'] %}
Time to Interactive — kiedy strona reaguje na klikniecia {{ tti.get('displayValue', '-') }}
{% endif %} {% if perf_data.get('total-blocking-time') %} {% set tbt = perf_data['total-blocking-time'] %}
Total Blocking Time — jak dlugo JS blokuje strone {{ tbt.get('displayValue', '-') }}
{% endif %}
{% endif %} {% if analysis.security_headers_count is not none %}

Zabezpieczenia

HSTS — wymuszenie HTTPS {% if analysis.has_hsts %}Tak{% else %}Nie{% endif %}
CSP — ochrona przed wstrzykiwaniem skryptow {% if analysis.has_csp %}Tak{% else %}Nie{% endif %}
X-Frame-Options — ochrona przed osadzaniem {% if analysis.has_x_frame_options %}Tak{% else %}Nie{% endif %}
X-Content-Type — ochrona typu plikow {% if analysis.has_x_content_type_options %}Tak{% else %}Nie{% endif %}
{% endif %}
{% if analysis.content_summary or analysis.services_extracted or analysis.main_keywords or analysis.word_count_homepage %}

Tresc strony i slowa kluczowe

{% if analysis.word_count_homepage %}
Liczba slow na stronie glownej {{ analysis.word_count_homepage }}
{% endif %} {% if analysis.content_richness_score %}
Bogactwo tresci {{ analysis.content_richness_score }}/10
{% endif %} {% if analysis.page_count_estimate %}
Szacowana liczba podstron {{ analysis.page_count_estimate }}
{% endif %} {% if analysis.google_indexed_pages %}
Stron zaindeksowanych w Google {{ analysis.google_indexed_pages }}
{% endif %}
Blog firmowy {% if analysis.has_blog %}Jest{% else %}Brak{% endif %}
Formularz kontaktowy {% if analysis.has_contact_form %}Jest{% else %}Brak{% endif %}
{% if analysis.has_portfolio is not none %}
Portfolio / Realizacje {% if analysis.has_portfolio %}Jest{% else %}Brak{% endif %}
{% endif %} {% if analysis.has_live_chat is not none %}
Live chat na stronie {% if analysis.has_live_chat %}Jest{% else %}Brak{% endif %}
{% endif %} {% if analysis.broken_links_count is not none %}
Zlamane linki {{ analysis.broken_links_count }}
{% endif %} {% if analysis.modern_image_ratio is not none %}
Nowoczesne formaty obrazkow (WebP/AVIF) {{ analysis.modern_image_ratio|round(0)|int }}%
{% endif %}
{% if analysis.h1_text %}
Naglowek H1 strony:
{{ analysis.h1_text }}
{% endif %} {% if analysis.content_summary %}
Podsumowanie tresci (AI):
{{ analysis.content_summary }}
{% endif %} {% if analysis.services_extracted %}
Wykryte uslugi:
{% for service in analysis.services_extracted %} {{ service }} {% endfor %}
{% endif %} {% if analysis.main_keywords %}
Glowne slowa kluczowe:
{% for kw in analysis.main_keywords %} {{ kw }} {% endfor %}
{% endif %} {% if analysis.structured_data_types %}
Typy danych strukturalnych (Schema.org):
{% for sdt in analysis.structured_data_types %} {{ sdt }} {% endfor %}
{% endif %}
{% endif %} {% if analysis.crux_lcp_ms or analysis.crux_inp_ms or analysis.crux_cls is not none %}

Core Web Vitals — dane od prawdziwych uzytkownikow (CrUX)

Te metryki pochodza z przegladarek Chrome prawdziwych odwiedzajacych, nie z testow laboratoryjnych. Wyniki moga sie roznic od Lighthouse.

{% if analysis.crux_lcp_ms %}
LCP — czas glownej tresci (p75) {{ (analysis.crux_lcp_ms / 1000)|round(1) }}s
{% endif %} {% if analysis.crux_fcp_ms %}
FCP — czas pierwszej tresci (p75) {{ (analysis.crux_fcp_ms / 1000)|round(1) }}s
{% endif %} {% if analysis.crux_inp_ms %}
INP — szybkosc reakcji (p75) {{ analysis.crux_inp_ms }}ms
{% endif %} {% if analysis.crux_cls is not none %}
CLS — stabilnosc wizualna (p75) {{ analysis.crux_cls }}
{% endif %} {% if analysis.crux_ttfb_ms %}
TTFB — czas odpowiedzi serwera (p75) {{ analysis.crux_ttfb_ms }}ms
{% endif %} {% if analysis.crux_lcp_good_pct is not none %}
Uzytkownicy z dobrym LCP {{ analysis.crux_lcp_good_pct }}%
{% endif %} {% if analysis.crux_inp_good_pct is not none %}
Uzytkownicy z dobrym INP {{ analysis.crux_inp_good_pct }}%
{% endif %}
{% endif %} {% if analysis.gsc_clicks or analysis.gsc_impressions %}

Google Search Console — widocznosc w wyszukiwarce

Dane z ostatnich {{ analysis.gsc_period_days or 28 }} dni. Pokazuja, jak strona wypada w wynikach wyszukiwania Google.

Klikniecia z Google {{ analysis.gsc_clicks or 0 }}
Wyswietlenia w Google {{ analysis.gsc_impressions or 0 }}
{% if analysis.gsc_ctr %}
CTR — procent klikniec {{ analysis.gsc_ctr }}%
{% endif %} {% if analysis.gsc_avg_position %}
Srednia pozycja w Google {{ analysis.gsc_avg_position }}
{% endif %} {% if analysis.gsc_index_status %}
Status indeksowania {{ analysis.gsc_index_status }}
{% endif %}
{% if analysis.gsc_top_queries and analysis.gsc_top_queries is not mapping %}
Top zapytania, po ktorych ludzie trafiaja na strone:
{% for q in analysis.gsc_top_queries[:10] %} {% endfor %}
Zapytanie Klikniecia Wyswietlenia Pozycja
{{ q.query if q.query is defined else q.get('keys', [''])[0] }} {{ q.clicks if q.clicks is defined else q.get('clicks', 0) }} {{ q.impressions if q.impressions is defined else q.get('impressions', 0) }} {{ "%.1f"|format(q.position if q.position is defined else q.get('position', 0)) }}
{% endif %} {% if analysis.gsc_top_pages and analysis.gsc_top_pages is not mapping %}
Najpopularniejsze podstrony w Google:
{% for p in analysis.gsc_top_pages[:10] %} {% endfor %}
Strona Klikniecia Wyswietlenia
{{ p.get('page', p.get('keys', [''])[0]) }} {{ p.get('clicks', 0) }} {{ p.get('impressions', 0) }}
{% endif %}
{% endif %} {% if analysis.google_rating or analysis.google_place_id %}

Google Business Profile

{% if analysis.google_rating %}
Ocena Google {{ analysis.google_rating }}/5 ({{ analysis.google_reviews_count or 0 }} opinii)
{% endif %} {% if analysis.google_name %}
Nazwa w Google {{ analysis.google_name }}
{% endif %} {% if analysis.google_address %}
Adres w Google {{ analysis.google_address }}
{% endif %} {% if analysis.google_phone %}
Telefon z Google {{ analysis.google_phone }}
{% endif %} {% if analysis.google_website %}
Strona WWW w Google {{ analysis.google_website }}
{% endif %} {% if analysis.google_business_status %}
Status {{ analysis.google_business_status }}
{% endif %} {% if analysis.google_types %}
Kategorie w Google {{ analysis.google_types|join(', ') }}
{% endif %} {% if analysis.google_photos_count %}
Zdjecia w Google {{ analysis.google_photos_count }}
{% endif %} {% if analysis.google_posts_count %}
Posty w Google (Google Posts) {{ analysis.google_posts_count }}
{% endif %} {% if analysis.google_review_response_rate is not none %}
Odpowiedzi na opinie {{ analysis.google_review_response_rate }}%
{% endif %} {% if analysis.google_maps_url %}
Link do Google Maps Otworz
{% endif %}
{% if analysis.google_editorial_summary %}
Opis firmy w Google:
{{ analysis.google_editorial_summary }}
{% endif %} {% if analysis.google_opening_hours %}
Godziny otwarcia z Google:
{% 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 %}
{{ entry }}
{% 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 %}
{{ 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:] }}
{% endfor %} {% elif hours_data is iterable and hours_data is not string and hours_data is not mapping %} {% for entry in hours_data %}
{{ entry }}
{% endfor %} {% else %}
{{ hours_data }}
{% endif %}
{% endif %} {% if analysis.google_reviews_data and analysis.google_reviews_data is iterable and analysis.google_reviews_data is not mapping %}
Ostatnie opinie Google:
{% for review in analysis.google_reviews_data[:5] %}
{{ review.get('author', review.get('authorAttribution', {}).get('displayName', 'Anonim')) }} {% set stars = review.get('rating', review.get('star_rating', 0))|int %} {% for i in range(stars) %}★{% endfor %}{% for i in range(5 - stars) %}☆{% endfor %}
{% set review_text = review.get('text', review.get('comment', review.get('originalText', {}).get('text', ''))) %} {% if review_text %}
{{ review_text[:200] }}{% if review_text|length > 200 %}...{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endif %} {% if analysis.gbp_impressions_maps or analysis.gbp_impressions_search or analysis.gbp_call_clicks %}

Statystyki Google Business Profile

Dane z ostatnich {{ analysis.gbp_performance_period_days or 30 }} dni.

{% if analysis.gbp_impressions_search %}
Wyswietlenia w wyszukiwarce Google {{ analysis.gbp_impressions_search }}
{% endif %} {% if analysis.gbp_impressions_maps %}
Wyswietlenia w Google Maps {{ analysis.gbp_impressions_maps }}
{% endif %} {% if analysis.gbp_website_clicks %}
Klikniecia w strone WWW {{ analysis.gbp_website_clicks }}
{% endif %} {% if analysis.gbp_call_clicks %}
Klikniecia w telefon {{ analysis.gbp_call_clicks }}
{% endif %} {% if analysis.gbp_direction_requests %}
Zapytania o dojazd {{ analysis.gbp_direction_requests }}
{% endif %} {% if analysis.gbp_conversations %}
Wiadomosci (conversations) {{ analysis.gbp_conversations }}
{% endif %}
{% if analysis.gbp_search_keywords and analysis.gbp_search_keywords is not mapping %}
Po jakich slowach ludzie szukaja tej firmy w Google Maps:
{% for kw in analysis.gbp_search_keywords[:15] %} {% endfor %}
Slowo kluczowe Wyswietlenia
{{ kw.get('keyword', kw.get('searchKeyword', kw)) if kw is mapping else kw }} {{ kw.get('impressions', kw.get('insightsValue', {}).get('value', '-')) if kw is mapping else '' }}
{% endif %}
{% endif %} {% if analysis.ssl_expires_at or analysis.final_url or analysis.hosting_provider or analysis.server_software or analysis.hosting_ip or domain_info %}

Infrastruktura i szczegoly techniczne

{% if domain_info.get('domain') %}
Domena {{ domain_info.domain }}
{% endif %} {% if domain_info.get('registered') %}
Domena zarejestrowana {{ domain_info.registered }}
{% endif %} {% if domain_info.get('expires') %}
Domena wygasa {{ domain_info.expires }}
{% endif %} {% if domain_info.get('updated') %}
Ostatnia zmiana domeny {{ domain_info.updated }}
{% endif %} {% if domain_info.get('registrar') %}
Rejestrator domeny (WHOIS) {{ domain_info.registrar }}
{% endif %} {% if analysis.final_url and analysis.final_url != analysis.website_url %}
URL po przekierowaniu {{ analysis.final_url }}
{% endif %} {% if analysis.http_status_code %}
Kod odpowiedzi HTTP {{ analysis.http_status_code }}
{% endif %} {% if analysis.ssl_expires_at %}
Certyfikat SSL wygasa {{ analysis.ssl_expires_at|local_time('%d.%m.%Y') }}
{% endif %} {% if analysis.ssl_issuer %}
Wystawca SSL {{ analysis.ssl_issuer }}
{% endif %} {% if analysis.hosting_provider or ip_info.get('isp') %}
Hosting {{ analysis.hosting_provider or ip_info.get('isp', '') }}
{% endif %} {% if ip_info.get('org') and ip_info.get('org') != (analysis.hosting_provider or '') %}
Firma hostingowa {{ ip_info.org }}
{% endif %} {% if analysis.hosting_ip %}
Adres IP serwera {{ analysis.hosting_ip }}
{% endif %} {% if ip_info.get('city') %}
Lokalizacja serwera {{ ip_info.city }}{% if ip_info.get('region') %}, {{ ip_info.region }}{% endif %}{% if ip_info.get('country') %}, {{ ip_info.country }}{% endif %}
{% endif %} {% if ip_info.get('as_number') %}
Siec (AS) {{ ip_info.as_number }}
{% endif %} {% if analysis.server_software %}
Oprogramowanie serwera {{ analysis.server_software }}
{% endif %} {% if analysis.site_author %}
Tworca strony {{ analysis.site_author }}
{% endif %} {% if analysis.site_generator %}
Generator strony {{ analysis.site_generator }}
{% endif %} {% if analysis.domain_registrar %}
Rejestrator domeny {{ analysis.domain_registrar }}
{% endif %} {% if analysis.frameworks_detected %}
Wykryte technologie {{ analysis.frameworks_detected|join(', ') }}
{% endif %} {% if analysis.last_modified_at %}
Ostatnia modyfikacja strony {{ analysis.last_modified_at|local_time('%d.%m.%Y %H:%M') }}
{% endif %}
{% endif %} {% if analysis and (analysis.audit_source or analysis.audit_errors or analysis.analyzed_at) %}

Diagnostyka audytu

Informacje techniczne o ostatnim uruchomieniu audytu.

{% if analysis.analyzed_at %}
Data audytu {{ analysis.analyzed_at|local_time('%d.%m.%Y %H:%M') }}
{% endif %} {% if analysis.audit_source %}
Zrodlo danych {{ analysis.audit_source }}
{% endif %} {% if analysis.audit_version %}
Wersja audytu {{ analysis.audit_version }}
{% endif %}
{% if analysis.audit_errors %}
Bledy podczas audytu:
{{ analysis.audit_errors }}
{% endif %}
{% 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 = '
' + 'Audyt zakonczony pomyslnie'; if (scores) { html += '
' + 'SEO: ' + (scores.seo_score || '-') + '' + 'Performance: ' + (scores.performance_score || '-') + '' + 'Dostepnosc: ' + (scores.accessibility_score || '-') + '' + 'Best Practices: ' + (scores.best_practices_score || '-') + '' + '
'; } html += '
' + '' + '' + '
'; 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 = '
' + 'Blad audytu' + '
' + (data.error || 'Nieznany blad') + '
' + '
' + '' + '
'; btn.disabled = false; btn.textContent = 'Uruchom audyt ponownie'; } }).catch(function(e) { apiDone = true; clearStepTimers(); setStep('step-connect', 'error'); result.style.display = 'block'; result.innerHTML = '
' + 'Blad polaczenia' + '
' + e.message + '
' + '
' + '' + '
'; btn.disabled = false; btn.textContent = 'Uruchom audyt ponownie'; }); } function hideAuditProgress() { document.getElementById('auditProgress').style.display = 'none'; } {% endblock %}