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

Audyt Google Business Profile

{{ company.name }}

Analiza kompletnosci wizytowki Google dla lokalnego SEO
Profil firmy {% if audit and (audit.google_maps_url or audit.google_place_id) %} {% set gbp_url = audit.google_maps_url if audit.google_maps_url else 'https://www.google.com/maps/search/?api=1&query=Google&query_place_id=' ~ audit.google_place_id %} Zobacz wizytowke Google {% endif %} {% if can_audit %} {% endif %}
{% if audit %} {# Unified 5-level color scale: 0-29 red, 30-49 orange, 50-69 amber, 70-89 lime, 90-100 green #} {% set score = audit.completeness_score %}
{{ score }} / 100
{% if score >= 90 %} Doskonaly profil GBP {% elif score >= 70 %} Dobry profil GBP {% elif score >= 50 %} Przecietny profil GBP {% elif score >= 30 %} Profil wymaga uzupelnienia {% else %} Slaby profil GBP {% endif %}

{% if audit.completeness_score >= 90 %} Twoja wizytowka Google jest bardzo dobrze zoptymalizowana. Utrzymaj wysoki standard i monitoruj opinie klientow. {% elif audit.completeness_score >= 70 %} Profil jest w dobrym stanie, ale sa obszary do poprawy. Skupienie sie na rekomendacjach zwiekszy widocznosc. {% elif audit.completeness_score >= 50 %} Wizytowka wymaga uzupelnienia. Wdrozenie ponizszych rekomendacji znaczaco poprawi lokalne SEO. {% else %} Wizytowka jest niekompletna i traci potencjalnych klientow. Priorytetowo uzupelnij brakujace informacje. {% endif %}

Ostatni audyt: {{ audit.audit_date.strftime('%d.%m.%Y %H:%M') if audit.audit_date else 'Brak danych' }}
{% if audit.review_count %}
{{ audit.review_count }} opinii{% if audit.average_rating %} ({{ audit.average_rating }}/5){% endif %}
{% endif %}

Status pol wizytowki

Kompletne
Czesciowe
Brakujace
{% set field_icons = { 'name': '', 'address': '', 'phone': '', 'website': '', 'hours': '', 'categories': '', 'photos': '', 'description': '', 'services': '', 'reviews': '' } %} {% set field_names_pl = { 'name': 'Nazwa firmy', 'address': 'Adres', 'phone': 'Telefon', 'website': 'Strona WWW', 'hours': 'Godziny otwarcia', 'categories': 'Kategorie', 'photos': 'Zdjecia', 'description': 'Opis', 'services': 'Uslugi', 'reviews': 'Opinie' } %} {% set status_names = { 'complete': 'Kompletne', 'partial': 'Czesciowe', 'missing': 'Brakuje' } %} {% for field_name, field_data in audit.fields_status.items() %}
{{ field_icons.get(field_name, '')|safe }} {{ field_names_pl.get(field_name, field_name) }} {{ status_names.get(field_data.status, field_data.status) }}
{% if field_data.value %}
{% if field_name == 'hours' and field_data.value is mapping and field_data.value.weekday_text %} {# Format opening hours nicely #} {% for day_hours in field_data.value.weekday_text[:5] %} {{ day_hours }}{% if not loop.last %}
{% endif %} {% endfor %} {% if field_data.value.weekday_text|length > 5 %}
... {% endif %} {% elif field_name == 'hours' and field_data.value is string and 'weekday_text' in field_data.value %} {# Handle string representation of dict #} Godziny ustawione {% else %} {{ field_data.value }} {% endif %}
{% endif %} {% if field_name == 'reviews' %}
{% if field_data.score == field_data.max_score %} 🏆 Doskonale! Masz maksymalna punktacje za opinie {% elif field_data.score == 0 %} 💡 Popros zadowolonych klientow o opinie w Google {% else %} 👍 Dobry poczatek! Zbierz wiecej opinii - cel to minimum 5 {% endif %} {% if field_data.details and field_data.details.breakdown %}
{{ field_data.details.breakdown }}
{% endif %}
{% endif %}
{{ field_data.score|round(1) }}/{{ field_data.max_score }}
{% endfor %}
{% if audit.recommendations %}

Rekomendacje ({{ audit.recommendations|length }})

{% for rec in audit.recommendations %}
{% if rec.priority == 'high' %} {% elif rec.priority == 'medium' %} {% else %} {% endif %}
{{ field_names_pl.get(rec.field, rec.field) }}
{{ rec.recommendation }}
+{{ rec.impact }} pkt
{% endfor %}
{% endif %}

Jak dziala wizytowka Google?

Wyszukiwarka Google

Gdy ktos szuka Twojej firmy w Google, po prawej stronie wynikow pojawia sie Panel Wiedzy (Knowledge Panel).

  • Nazwa i logo firmy
  • Adres i godziny otwarcia
  • Ocena i opinie
  • Przycisk "Zadzwon" i "Trasa"

Mapy Google

W aplikacji Google Maps Twoja firma ma pelna wizytowke z dodatkowymi funkcjami.

  • Zdjecia i wirtualny spacer
  • Wszystkie opinie klientow
  • Pytania i odpowiedzi (Q&A)
  • Posty i aktualnosci firmy

Jak zarzadzac?

Wszystkie dane edytujesz w jednym miejscu - panelu Google Business Profile.

  • Wejdz na business.google.com
  • Zaloguj sie kontem Google
  • Edytuj dane - zaktualizuja sie wszedzie
  • Odpowiadaj na opinie klientow

Jedno zrodlo, wiele widokow. Panel Wiedzy w wyszukiwarce i wizytowka w Mapach Google to te same dane wyswietlane w roznych miejscach. Wystarczy, ze zarzadzasz profilem w Google Business Profile - zmiany automatycznie pojawia sie wszedzie: w wyszukiwarce, Mapach, Asystencie Google i wynikach lokalnych.

{% else %}

Brak danych audytu

Nie przeprowadzono jeszcze audytu wizytowki Google dla tej firmy. Uruchom audyt, aby sprawdzic kompletnosc profilu.

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

Audyt Google Business Profile

Pobieram dane z Google i analizuje wizytowke...

Szukam firmy w Google Maps...
Ocena Google
Liczba opinii Google
Zdjecia Google
Godziny otwarcia Google
Numer telefonu Google
Strona WWW Google
Status firmy Google
Zapisuje dane w bazie
Analizuje kompletnosc profilu
{% endblock %} {% block extra_js %} const csrfToken = '{{ csrf_token() }}'; const companySlug = '{{ company.slug }}'; // All step IDs in order const allSteps = [ 'step-find', 'step-rating', 'step-reviews', 'step-photos', 'step-hours', 'step-phone', 'step-website', 'step-status', 'step-save', 'step-audit' ]; // Detail step labels (defaults) with source tags const detailLabels = { 'step-rating': 'Ocena Google', 'step-reviews': 'Liczba opinii Google', 'step-photos': 'Zdjecia Google', 'step-hours': 'Godziny otwarcia Google', 'step-phone': 'Numer telefonu Google', 'step-website': 'Strona WWW Google', 'step-status': 'Status firmy Google' }; // SVG icons for different states const icons = { pending: '', in_progress: '
', complete: '', error: '', warning: '', skipped: '', missing: '' }; function resetSteps() { // Reset all steps to default labels and pending state allSteps.forEach((stepId, index) => { const stepEl = document.getElementById(stepId); if (stepEl) { const iconEl = stepEl.querySelector('.step-icon'); const textEl = stepEl.querySelector('.step-text'); // Reset label to default (use innerHTML to preserve source tags) if (detailLabels[stepId]) { textEl.innerHTML = detailLabels[stepId]; } if (index === 0) { iconEl.className = 'step-icon in_progress'; iconEl.innerHTML = icons.in_progress; textEl.className = 'step-text in_progress'; } else { iconEl.className = 'step-icon pending'; iconEl.innerHTML = icons.pending; textEl.className = 'step-text pending'; } } }); } function updateStep(stepId, status, message) { const stepEl = document.getElementById(stepId); if (!stepEl) return; const iconEl = stepEl.querySelector('.step-icon'); const textEl = stepEl.querySelector('.step-text'); iconEl.className = 'step-icon ' + status; iconEl.innerHTML = icons[status] || icons.pending; textEl.className = 'step-text ' + status; if (message) { textEl.textContent = message; } } function showLoading() { resetSteps(); 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(); }); // Update detail steps with fetched data values async function updateDetailSteps(googleData) { const delay = 150; // ms between each step animation // Rating updateStep('step-rating', 'in_progress', 'Pobieram ocene...'); await new Promise(r => setTimeout(r, delay)); if (googleData.google_rating) { updateStep('step-rating', 'complete', `Ocena: ${googleData.google_rating}/5`); } else { updateStep('step-rating', 'missing', 'Brak oceny'); } // Reviews updateStep('step-reviews', 'in_progress', 'Pobieram opinie...'); await new Promise(r => setTimeout(r, delay)); if (googleData.google_reviews_count) { updateStep('step-reviews', 'complete', `Opinie: ${googleData.google_reviews_count}`); } else { updateStep('step-reviews', 'missing', 'Brak opinii'); } // Photos updateStep('step-photos', 'in_progress', 'Pobieram zdjecia...'); await new Promise(r => setTimeout(r, delay)); if (googleData.google_photos_count) { updateStep('step-photos', 'complete', `Zdjecia: ${googleData.google_photos_count}`); } else { updateStep('step-photos', 'missing', 'Brak zdjec'); } // Opening hours updateStep('step-hours', 'in_progress', 'Pobieram godziny otwarcia...'); await new Promise(r => setTimeout(r, delay)); if (googleData.google_opening_hours && googleData.google_opening_hours.weekday_text) { updateStep('step-hours', 'complete', 'Godziny otwarcia: ustawione'); } else { updateStep('step-hours', 'missing', 'Brak godzin otwarcia'); } // Phone updateStep('step-phone', 'in_progress', 'Pobieram telefon...'); await new Promise(r => setTimeout(r, delay)); if (googleData.google_phone) { updateStep('step-phone', 'complete', `Telefon: ${googleData.google_phone}`); } else { updateStep('step-phone', 'missing', 'Brak telefonu'); } // Website updateStep('step-website', 'in_progress', 'Pobieram strone WWW...'); await new Promise(r => setTimeout(r, delay)); if (googleData.google_website) { // Truncate long URLs const shortUrl = googleData.google_website.replace(/^https?:\/\//, '').slice(0, 30); updateStep('step-website', 'complete', `WWW: ${shortUrl}...`); } else { updateStep('step-website', 'missing', 'Brak strony WWW'); } // Business status updateStep('step-status', 'in_progress', 'Pobieram status...'); await new Promise(r => setTimeout(r, delay)); if (googleData.google_business_status) { const statusMap = { 'OPERATIONAL': 'Czynna', 'CLOSED_TEMPORARILY': 'Tymczasowo zamknieta', 'CLOSED_PERMANENTLY': 'Zamknieta na stale' }; const statusText = statusMap[googleData.google_business_status] || googleData.google_business_status; updateStep('step-status', 'complete', `Status: ${statusText}`); } else { updateStep('step-status', 'missing', 'Brak statusu'); } } async function runAudit() { const btn = document.getElementById('runAuditBtn'); if (btn) { btn.disabled = true; } showLoading(); // Simulate step animation start await new Promise(r => setTimeout(r, 300)); try { const response = await fetch('/api/gbp/audit', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ slug: companySlug, force_refresh: true }) }); const data = await response.json(); // Handle find_place step if (data.google_fetch && data.google_fetch.steps) { const findStep = data.google_fetch.steps.find(s => s.step === 'find_place'); if (findStep) { updateStep('step-find', findStep.status, findStep.message); } } // If we have Google data, animate the detail steps with actual values if (data.google_fetch && data.google_fetch.data && data.google_fetch.success) { await updateDetailSteps(data.google_fetch.data); // Save step const saveStep = data.google_fetch.steps.find(s => s.step === 'save_data'); if (saveStep) { updateStep('step-save', saveStep.status, saveStep.message); } } else if (data.google_fetch && data.google_fetch.steps) { // Mark detail steps as skipped if no Google data const detailStepIds = ['step-rating', 'step-reviews', 'step-photos', 'step-hours', 'step-phone', 'step-website', 'step-status']; for (const stepId of detailStepIds) { updateStep(stepId, 'skipped', detailLabels[stepId] + ' (pominiety)'); } } // Update audit step if (response.ok && data.success) { updateStep('step-save', 'complete', 'Dane zapisane'); updateStep('step-audit', 'complete', `Analiza zakonczona: ${data.audit?.total_score || 0}/100`); // Wait 5 seconds so user can read the progress steps await new Promise(r => setTimeout(r, 5000)); hideLoading(); showInfoModal('Audyt zakonczony', 'Audyt wizytowki Google zostal zakonczony pomyslnie. Strona zostanie odswiezona.', true); setTimeout(() => location.reload(), 1500); } else { updateStep('step-audit', 'error', 'Blad audytu'); // Wait 5 seconds so user can see what failed await new Promise(r => setTimeout(r, 5000)); hideLoading(); 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; } } {% endblock %}