feat: add GBP OAuth connection status display to audit page
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

Shows whether the company has an active Google Business Profile
console connection, with clear status indicators (connected/expired/
not connected) and guidance on how to reconnect via Konto → Integracje.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-03-11 07:21:36 +01:00
parent 6732f5da74
commit c03a2ad055
2 changed files with 83 additions and 1 deletions

View File

@ -502,6 +502,27 @@ def gbp_audit_dashboard(slug):
# Determine if user can run audit (user with company edit rights)
can_audit = current_user.can_edit_company(company.id)
# Check GBP OAuth connection status
gbp_oauth_token = db.query(OAuthToken).filter(
OAuthToken.company_id == company.id,
OAuthToken.provider == 'google',
OAuthToken.service == 'gbp'
).first()
gbp_connection = None
if gbp_oauth_token:
from datetime import datetime
is_expired = gbp_oauth_token.expires_at and gbp_oauth_token.expires_at < datetime.utcnow()
has_location = bool(gbp_oauth_token.metadata_json and isinstance(gbp_oauth_token.metadata_json, dict) and gbp_oauth_token.metadata_json.get('location_name'))
gbp_connection = {
'connected': True,
'is_active': gbp_oauth_token.is_active,
'is_expired': is_expired,
'has_location': has_location,
'created_at': gbp_oauth_token.created_at,
'expires_at': gbp_oauth_token.expires_at,
'has_refresh_token': bool(gbp_oauth_token.refresh_token),
}
logger.info(f"GBP audit dashboard viewed by {current_user.email} for company: {company.name}")
return render_template('gbp_audit.html',
@ -513,7 +534,8 @@ def gbp_audit_dashboard(slug):
gbp_audit_available=GBP_AUDIT_AVAILABLE,
gbp_audit_version=GBP_AUDIT_VERSION,
gbp_recommendations=gbp_recommendations if analysis else [],
gbp_benchmarks=gbp_benchmarks if analysis else {}
gbp_benchmarks=gbp_benchmarks if analysis else {},
gbp_connection=gbp_connection
)
finally:

View File

@ -1988,6 +1988,66 @@
</div>
{% endif %}
{# GBP Console Connection Status #}
<div style="background: var(--surface); padding: var(--spacing-lg); border-radius: var(--radius-lg); box-shadow: var(--shadow); margin-bottom: var(--spacing-md);">
<h2 class="section-title">
<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.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>
</svg>
Połączenie z konsolą Google Business Profile
</h2>
{% if gbp_connection and gbp_connection.connected %}
{% if gbp_connection.is_active and not gbp_connection.is_expired %}
{# Active and valid connection #}
<div style="display: flex; align-items: center; gap: var(--spacing-sm); padding: var(--spacing-md); background: rgba(34, 197, 94, 0.08); border: 1px solid rgba(34, 197, 94, 0.2); border-radius: var(--radius);">
<span style="font-size: 20px;"></span>
<div>
<strong style="color: #16a34a;">Połączone</strong>
<p style="margin: 4px 0 0; font-size: var(--font-size-xs); color: var(--text-secondary);">
Konsola GBP jest aktywna. Dostępne są dane o wyświetleniach, wyszukiwaniach i interakcjach klientów.
</p>
</div>
</div>
{% elif gbp_connection.is_expired or not gbp_connection.is_active %}
{# Expired or inactive connection #}
<div style="display: flex; align-items: center; gap: var(--spacing-sm); padding: var(--spacing-md); background: rgba(245, 158, 11, 0.08); border: 1px solid rgba(245, 158, 11, 0.2); border-radius: var(--radius);">
<span style="font-size: 20px;"></span>
<div>
<strong style="color: #d97706;">Połączenie wygasło</strong>
<p style="margin: 4px 0 0; font-size: var(--font-size-xs); color: var(--text-secondary);">
Autoryzacja Google wygasła{% if gbp_connection.expires_at %} ({{ gbp_connection.expires_at.strftime('%d.%m.%Y') }}){% endif %}.
{% if gbp_connection.has_refresh_token %}
Token odświeżający jest dostępny, ale wymaga ponownej autoryzacji.
{% endif %}
Właściciel firmy powinien ponownie połączyć konto w panelu <strong>Konto → Integracje</strong>.
</p>
</div>
</div>
{% endif %}
<div style="margin-top: var(--spacing-sm); font-size: var(--font-size-xs); color: var(--text-tertiary);">
Połączone od: {{ gbp_connection.created_at.strftime('%d.%m.%Y') if gbp_connection.created_at else '—' }}
{% if gbp_connection.has_location %} · Lokalizacja GBP skonfigurowana{% endif %}
</div>
{% else %}
{# No connection at all #}
<div style="display: flex; align-items: center; gap: var(--spacing-sm); padding: var(--spacing-md); background: rgba(107, 114, 128, 0.06); border: 1px solid var(--border); border-radius: var(--radius);">
<span style="font-size: 20px; color: var(--text-tertiary);"></span>
<div>
<strong style="color: var(--text-secondary);">Brak połączenia</strong>
<p style="margin: 4px 0 0; font-size: var(--font-size-xs); color: var(--text-secondary);">
Konsola Google Business Profile nie jest połączona. Dane audytu pochodzą wyłącznie z publicznego API Google Places.
Po połączeniu konta będą dostępne dodatkowe statystyki: wyświetlenia w Google, kliknięcia, połączenia telefoniczne i nawigacje.
</p>
<p style="margin: 6px 0 0; font-size: var(--font-size-xs); color: var(--text-tertiary);">
Właściciel firmy może połączyć konto w panelu <strong>Konto → Integracje</strong>.
</p>
</div>
</div>
{% endif %}
</div>
{# Google Reviews from Places API — data is dict with 'reviews' key #}
{% if places_data and places_data.google_reviews_data is mapping and places_data.google_reviews_data.get('reviews') %}
{% set api_reviews = places_data.google_reviews_data.reviews %}