{% extends "base.html" %} {% block title %}Audyt SEO - {{ company.name }} - Norda Biznes Partner{% endblock %} {% block extra_css %} {% endblock %} {% block content %}

Audyt SEO Strony WWW

{{ company.name }}

Analiza SEO i wydajnosci strony WWW (Google PageSpeed Insights)
{% if company.website %} {% endif %}
Profil firmy {% if can_audit and company.website %} {% endif %}
{% if seo_data %} {# Unified 5-level color scale: 0-29 red, 30-49 orange, 50-69 amber, 70-89 lime, 90-100 green #} {% set score = seo_data.seo_score or 0 %}
{{ score }} / 100
{% if score >= 90 %} Doskonaly wynik SEO (Google Lighthouse) {% elif score >= 70 %} Dobry wynik SEO (Google Lighthouse) {% elif score >= 50 %} Przecietny wynik SEO (Google Lighthouse) {% elif score >= 30 %} Wynik SEO wymaga poprawy (Google Lighthouse) {% else %} Slaby wynik SEO (Google Lighthouse) {% endif %}

{% if score >= 90 %} Strona jest bardzo dobrze zoptymalizowana pod katem SEO. Utrzymuj wysoki standard i monitoruj zmiany. {% elif score >= 70 %} Strona ma dobra optymalizacje SEO, ale sa obszary do poprawy. Skup sie na wydajnosci i dostepnosci. {% elif score >= 50 %} Strona wymaga pracy nad optymalizacja SEO. Warto poprawic wydajnosc i dostepnosc. {% else %} Strona ma powazne problemy z SEO. Priorytetowo popraw wydajnosc i optymalizacje. {% endif %}

Wynik pochodzi z Google PageSpeed Insights i ocenia techniczne aspekty SEO (meta tagi, robots.txt, indeksowalnosc). Pelna ocena SEO, wlaczajac lokalne SEO i widocznosc, jest dostepna w analizie AI ponizej.

Ostatni audyt: {{ seo_data.audited_at.strftime('%d.%m.%Y %H:%M') if seo_data.audited_at else 'Brak danych' }}
{% if seo_data.url %}
{{ seo_data.url|truncate(40) }}
{% endif %}

Szczegolowe metryki

{% set seo = seo_data.seo_score %} {% set seo_class = 'good' if seo and seo >= 90 else ('medium' if seo and seo >= 50 else ('poor' if seo else 'none')) %}
Wynik SEO
{{ seo if seo else '-' }}
{% set perf = seo_data.performance_score %} {% set perf_class = 'good' if perf and perf >= 90 else ('medium' if perf and perf >= 50 else ('poor' if perf else 'none')) %}
Wydajnosc
{{ perf if perf else '-' }}
{% set acc = seo_data.accessibility_score %} {% set acc_class = 'good' if acc and acc >= 90 else ('medium' if acc and acc >= 50 else ('poor' if acc else 'none')) %}
Dostepnosc
{{ acc if acc else '-' }}
{% set bp = seo_data.best_practices_score %} {% set bp_class = 'good' if bp and bp >= 90 else ('medium' if bp and bp >= 50 else ('poor' if bp else 'none')) %}
Best Practices
{{ bp if bp else '-' }}
{% if seo_data.lcp_ms is not none or seo_data.inp_ms is not none or seo_data.cls is not none %}

Core Web Vitals

{% if seo_data.lcp_ms is not none %} {% set lcp = seo_data.lcp_ms %} {% set lcp_class = 'good' if lcp < 2500 else ('medium' if lcp < 4000 else 'poor') %}
LCP
{{ '%.1f'|format(lcp / 1000) }}s
Largest Contentful Paint
{% endif %} {% if seo_data.inp_ms is not none %} {% set inp = seo_data.inp_ms %} {% set inp_class = 'good' if inp <= 200 else ('medium' if inp <= 500 else 'poor') %}
INP
{{ inp }}ms
Interaction to Next Paint
{% endif %} {% if seo_data.cls is not none %} {% set cls = seo_data.cls %} {% set cls_class = 'good' if cls < 0.1 else ('medium' if cls < 0.25 else 'poor') %}
CLS
{{ '%.3f'|format(cls) }}
Cumulative Layout Shift
{% endif %}
{% endif %} {% if seo_data.local_seo_score is not none %}

Local SEO

{% set lscore = seo_data.local_seo_score %}
{{ lscore }}
{% if lscore >= 70 %}Dobry Local SEO{% elif lscore >= 40 %}Przecietny Local SEO{% else %}Slaby Local SEO{% endif %}

Ocena optymalizacji pod lokalne wyszukiwanie

{{ '✓' if seo_data.has_local_business_schema else '✗' }} Schema.org LocalBusiness
{{ '✓' if seo_data.has_google_maps_embed else '✗' }} Mapa Google na stronie
{{ '✓' if seo_data.has_local_keywords else '✗' }} Lokalne slowa kluczowe
{% if seo_data.nap_on_website %}
NAP na stronie (Nazwa, Adres, Telefon)
{% else %}
NAP na stronie (Nazwa, Adres, Telefon)
{% endif %}
{% if seo_data.local_keywords_found %}
Znalezione slowa kluczowe:
{% for kw in seo_data.local_keywords_found[:10] %} {{ kw }} {% endfor %}
{% endif %}
{% endif %} {% if seo_data.content_freshness_score is not none %}
{% set fresh = seo_data.content_freshness_score %} {% set fresh_class = 'good' if fresh >= 70 else ('medium' if fresh >= 40 else 'poor') %}
Swiezosc tresci
{{ fresh }}
{% if seo_data.last_modified_date %}
Ostatnia modyfikacja
{{ seo_data.last_modified_date.strftime('%d.%m.%Y') }}
{% endif %}
{% endif %} {% if seo_data.citations and seo_data.citations|length > 0 %}

Cytacje lokalne ({{ seo_data.citations_count or seo_data.citations|length }})

{% for citation in seo_data.citations %}
{{ citation.directory_name }}
{% if citation.status == 'found' %} Znaleziono {% if citation.listing_url %} Link {% endif %} {% elif citation.status == 'incorrect' %} Bledne dane {% else %} Nie znaleziono {% endif %}
{% endfor %}
{% endif %} {% if seo_data.has_ssl is not none or seo_data.has_google_analytics is not none or seo_data.h1_count is not none or seo_data.total_images is not none %}

Technical SEO

{% if seo_data.has_ssl is not none %}
{{ '✓' if seo_data.has_ssl else '✗' }} Certyfikat SSL{% if seo_data.has_ssl and seo_data.ssl_expires_at %} (wazny do {{ seo_data.ssl_expires_at.strftime('%d.%m.%Y') }}){% endif %}
{% endif %} {% if seo_data.has_google_analytics is not none %}
{{ '✓' if seo_data.has_google_analytics else '✗' }} Google Analytics
{% endif %} {% if seo_data.has_google_tag_manager is not none %}
{{ '✓' if seo_data.has_google_tag_manager else '✗' }} Google Tag Manager
{% endif %} {% if seo_data.has_og_tags is not none %}
{{ '✓' if seo_data.has_og_tags else '✗' }} Open Graph Tags
{% endif %} {% if seo_data.has_twitter_cards is not none %}
{{ '✓' if seo_data.has_twitter_cards else '✗' }} Twitter Cards
{% endif %} {% if seo_data.h1_count is not none %} {% set h1_ok = seo_data.h1_count == 1 %}
{{ '✓' if h1_ok else '⚠' }} H1: {{ seo_data.h1_count }}{% if not h1_ok %} (powinien byc 1){% endif %}{% if seo_data.h2_count is not none %}, H2: {{ seo_data.h2_count }}{% endif %}{% if seo_data.h3_count is not none %}, H3: {{ seo_data.h3_count }}{% endif %}
{% endif %} {% if seo_data.total_images is not none %} {% set alt_ok = (seo_data.images_without_alt or 0) == 0 %}
{{ '✓' if alt_ok else '⚠' }} Obrazy: {{ seo_data.total_images }}{% if not alt_ok %} ({{ seo_data.images_without_alt }} bez alt){% else %} (wszystkie z alt){% endif %}
{% endif %} {% if seo_data.internal_links_count is not none or seo_data.external_links_count is not none %} {% set broken = seo_data.broken_links_count or 0 %}
{{ '✓' if broken == 0 else '✗' }} Linki: {{ seo_data.internal_links_count or 0 }} wew., {{ seo_data.external_links_count or 0 }} zew.{% if broken > 0 %}, {{ broken }} uszkodzonych{% endif %}
{% endif %}
{% if seo_data.h1_text %}
Tytul H1: {{ seo_data.h1_text }}
{% endif %}
{% endif %} {% else %}

Brak danych audytu SEO

{% if company.website %}

Nie przeprowadzono jeszcze audytu SEO dla strony tej firmy. Uruchom audyt, aby sprawdzic optymalizacje strony.

{% if can_audit %} {% endif %} {% else %}

Ta firma nie ma zdefiniowanej strony WWW. Dodaj adres strony w profilu firmy, aby moc przeprowadzic audyt SEO.

Przejdz do profilu firmy {% endif %}
{% endif %} {% if seo_data %} {% with audit_type='seo' %} {% include 'partials/audit_ai_actions.html' %} {% endwith %} {% endif %}

Analiza SEO w toku...

Pobieranie danych z Google PageSpeed Insights. Moze to potrwac do 30 sekund.

{% endblock %} {% block extra_js %} const csrfToken = '{{ csrf_token() }}'; const companySlug = '{{ company.slug }}'; function showLoading() { document.getElementById('loadingOverlay').classList.add('active'); } function hideLoading() { document.getElementById('loadingOverlay').classList.remove('active'); } function showInfoModal(title, body, isSuccess) { document.getElementById('modalTitle').textContent = title; document.getElementById('modalBody').textContent = body; const icon = document.getElementById('modalIcon'); icon.className = 'modal-icon ' + (isSuccess ? 'success' : 'info'); document.getElementById('infoModal').classList.add('active'); } function closeInfoModal() { document.getElementById('infoModal').classList.remove('active'); } document.getElementById('infoModal')?.addEventListener('click', (e) => { if (e.target.id === 'infoModal') closeInfoModal(); }); async function runAudit() { const btn = document.getElementById('runAuditBtn'); if (btn) { btn.disabled = true; } showLoading(); try { const response = await fetch('/api/seo/audit', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ slug: companySlug }) }); const data = await response.json(); hideLoading(); if (response.ok && data.success) { showInfoModal('Audyt zakonczony', 'Audyt SEO zostal zakonczony pomyslnie. Strona zostanie odswiezona.', true); setTimeout(() => location.reload(), 1500); } else { showInfoModal('Blad', data.error || 'Wystapil nieznany blad podczas audytu.', false); if (btn) btn.disabled = false; } } catch (error) { hideLoading(); showInfoModal('Blad polaczenia', 'Nie udalo sie polaczyc z serwerem: ' + error.message, false); if (btn) btn.disabled = false; } } /* ============================================================ AI AUDIT ACTIONS ============================================================ */ const companyId = {{ company.id }}; const auditType = 'seo'; function simpleMarkdown(text) { return text .replace(/&/g, '&').replace(//g, '>') .replace(/\*\*(.+?)\*\*/g, '$1') .replace(/\*(.+?)\*/g, '$1') .replace(/^### (.+)$/gm, '

$1

') .replace(/^## (.+)$/gm, '

$1

') .replace(/^- (.+)$/gm, '
  • $1
  • ') .replace(/(
  • .*<\/li>)/gs, '') .replace(/\n/g, '
    '); } async function runAIAnalysis(force) { const prompt = document.getElementById('aiAnalyzePrompt'); const loading = document.getElementById('aiLoading'); const results = document.getElementById('aiResults'); const btn = document.getElementById('aiAnalyzeBtn'); if (btn) btn.disabled = true; if (prompt) prompt.style.display = 'none'; if (results) results.style.display = 'none'; if (loading) loading.style.display = 'block'; // Start timer let seconds = 0; const timerEl = document.getElementById('aiTimer'); const timerInterval = setInterval(() => { seconds++; if (timerEl) timerEl.textContent = seconds + 's'; }, 1000); try { const response = await fetch('/api/audit/analyze', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ company_id: companyId, audit_type: auditType, force: !!force }) }); const data = await response.json(); clearInterval(timerInterval); if (loading) loading.style.display = 'none'; if (data.success) { renderAIResults(data); } else { if (prompt) prompt.style.display = 'none'; if (btn) btn.disabled = false; const results = document.getElementById('aiResults'); results.innerHTML = `

    Blad analizy AI

    ${escapeHtml(data.error || 'Nieznany blad')}

    `; results.style.display = 'block'; } } catch (error) { clearInterval(timerInterval); if (loading) loading.style.display = 'none'; if (prompt) prompt.style.display = 'none'; if (btn) btn.disabled = false; const results = document.getElementById('aiResults'); results.innerHTML = `

    Blad polaczenia

    ${escapeHtml(error.message)}

    `; results.style.display = 'block'; } } function renderAIResults(data) { const results = document.getElementById('aiResults'); const summaryEl = document.getElementById('aiSummaryText'); const cacheInfo = document.getElementById('aiCacheInfo'); const actionsList = document.getElementById('aiActionsList'); summaryEl.textContent = data.summary || ''; if (data.cached && data.generated_at) { const d = new Date(data.generated_at); document.getElementById('aiCacheDate').textContent = d.toLocaleDateString('pl-PL') + ' ' + d.toLocaleTimeString('pl-PL', {hour:'2-digit', minute:'2-digit'}); cacheInfo.style.display = 'block'; } else { cacheInfo.style.display = 'none'; } actionsList.innerHTML = ''; const actions = data.actions || []; const priorityLabels = {critical: 'KRYTYCZNE', high: 'WYSOKI', medium: 'SREDNI', low: 'NISKI'}; actions.forEach((action, idx) => { const card = document.createElement('div'); card.className = 'ai-action-card priority-' + (action.priority || 'medium'); card.id = 'ai-action-' + idx; const impact = action.impact_score || 5; const effort = action.effort_score || 5; card.innerHTML = `
    ${priorityLabels[action.priority] || 'SREDNI'} ${escapeHtml(action.title || '')}

    ${escapeHtml(action.description || '')}

    Wplyw: ${impact}/10
    Wysilek: ${effort}/10
    `; actionsList.appendChild(card); }); // Render comparison with previous analysis if available if (typeof renderAIComparison === 'function') renderAIComparison(data); results.style.display = 'block'; document.getElementById('aiActionsSection').scrollIntoView({behavior: 'smooth', block: 'start'}); // Store actions data for content generation window._aiActions = actions; } async function generateContent(actionType, idx) { const container = document.getElementById('ai-content-' + idx); if (!container) return; // If already has content, toggle visibility if (container.dataset.loaded === 'true') { container.style.display = container.style.display === 'none' ? 'block' : 'none'; return; } container.innerHTML = '
    Generowanie tresci...
    '; container.style.display = 'block'; try { const response = await fetch('/api/audit/generate-content', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ company_id: companyId, action_type: actionType, context: {} }) }); const data = await response.json(); if (data.success && data.content) { const isCode = data.content.includes('{') && (data.content.includes(' ${escapeHtml(data.content)} `; } else { container.innerHTML = `
    ${simpleMarkdown(data.content)}
    `; } container.dataset.loaded = 'true'; container.scrollIntoView({behavior: 'smooth', block: 'nearest'}); } else { container.innerHTML = `
    ${escapeHtml(data.error || 'Blad generowania')}
    `; } } catch (error) { container.innerHTML = `
    ${escapeHtml(error.message)}
    `; } } function copyContent(btn) { const code = btn.parentElement.querySelector('code') || btn.parentElement.querySelector('.ai-markdown-content'); if (!code) return; navigator.clipboard.writeText(code.textContent).then(() => { const orig = btn.textContent; btn.textContent = 'Skopiowano!'; setTimeout(() => { btn.textContent = orig; }, 2000); }); } function markAction(idx, status) { const card = document.getElementById('ai-action-' + idx); if (!card) return; if (status === 'implemented') { card.classList.add('implemented'); } else if (status === 'dismissed') { card.classList.add('dismissed'); } // Fire and forget status update to backend const actions = window._aiActions || []; if (actions[idx] && actions[idx].id) { fetch('/api/audit/actions/' + actions[idx].id + '/status', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ status: status }) }).catch(() => {}); } } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } {% endblock %}