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
New page /admin/gbp-audit/match-places shows companies without google_place_id. Admin searches Google Maps, reviews results, and confirms the correct match. Adds search_places_raw() to return all results without name filtering. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
261 lines
9.9 KiB
HTML
261 lines
9.9 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Dopasuj firmy do Google Maps - Norda Biznes Partner{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.match-page { max-width: 1100px; margin: 0 auto; }
|
|
|
|
.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); }
|
|
|
|
.match-header {
|
|
margin-bottom: var(--spacing-xl);
|
|
}
|
|
.match-header h1 { font-size: var(--font-size-2xl); color: var(--text-primary); margin-bottom: var(--spacing-xs); }
|
|
.match-header p { color: var(--text-secondary); }
|
|
|
|
.company-card {
|
|
background: white; border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
|
|
margin-bottom: var(--spacing-md); overflow: hidden;
|
|
}
|
|
.company-card.matched { opacity: 0.5; }
|
|
|
|
.company-header {
|
|
display: flex; justify-content: space-between; align-items: center;
|
|
padding: var(--spacing-md) var(--spacing-lg);
|
|
cursor: default;
|
|
}
|
|
|
|
.company-info { flex: 1; }
|
|
.company-name { font-weight: 600; font-size: var(--font-size-lg); color: var(--text-primary); }
|
|
.company-city { font-size: var(--font-size-sm); color: var(--text-secondary); margin-top: 2px; }
|
|
|
|
.btn-search {
|
|
background: var(--primary); color: white; border: none; padding: var(--spacing-sm) var(--spacing-lg);
|
|
border-radius: var(--radius-md); cursor: pointer; font-size: var(--font-size-sm); font-weight: 500;
|
|
white-space: nowrap;
|
|
}
|
|
.btn-search:hover { opacity: 0.9; }
|
|
.btn-search:disabled { background: var(--secondary); cursor: not-allowed; }
|
|
|
|
.btn-skip {
|
|
background: transparent; color: var(--text-secondary); border: 1px solid var(--border-color);
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
border-radius: var(--radius-md); cursor: pointer; font-size: var(--font-size-sm);
|
|
margin-left: var(--spacing-sm);
|
|
}
|
|
.btn-skip:hover { background: var(--bg-secondary); }
|
|
|
|
.search-results {
|
|
padding: 0 var(--spacing-lg) var(--spacing-lg);
|
|
display: none;
|
|
}
|
|
.search-results.visible { display: block; }
|
|
|
|
.search-loading {
|
|
text-align: center; padding: var(--spacing-lg); color: var(--text-secondary);
|
|
}
|
|
|
|
.place-result {
|
|
display: flex; justify-content: space-between; align-items: center;
|
|
padding: var(--spacing-md); border: 1px solid var(--border-color);
|
|
border-radius: var(--radius-md); margin-bottom: var(--spacing-sm);
|
|
gap: var(--spacing-md);
|
|
}
|
|
.place-result:hover { border-color: var(--primary); background: var(--bg-primary); }
|
|
|
|
.place-info { flex: 1; }
|
|
.place-name { font-weight: 600; color: var(--text-primary); }
|
|
.place-address { font-size: var(--font-size-sm); color: var(--text-secondary); margin-top: 2px; }
|
|
.place-meta { font-size: var(--font-size-xs); color: var(--text-tertiary); margin-top: 4px; }
|
|
.place-meta .rating { color: #f59e0b; font-weight: 600; }
|
|
|
|
.btn-confirm {
|
|
background: #16a34a; color: white; border: none; padding: var(--spacing-sm) var(--spacing-lg);
|
|
border-radius: var(--radius-md); cursor: pointer; font-size: var(--font-size-sm); font-weight: 500;
|
|
white-space: nowrap;
|
|
}
|
|
.btn-confirm:hover { background: #15803d; }
|
|
.btn-confirm:disabled { background: var(--secondary); cursor: not-allowed; }
|
|
|
|
.no-results {
|
|
text-align: center; padding: var(--spacing-lg); color: var(--text-secondary);
|
|
font-style: italic;
|
|
}
|
|
|
|
.match-badge {
|
|
display: inline-block; background: #16a34a; color: white; font-size: var(--font-size-xs);
|
|
padding: 2px 8px; border-radius: var(--radius-full); font-weight: 600;
|
|
}
|
|
|
|
.counter {
|
|
background: var(--bg-secondary); padding: var(--spacing-md) var(--spacing-lg);
|
|
border-radius: var(--radius-lg); margin-bottom: var(--spacing-xl);
|
|
display: flex; gap: var(--spacing-xl); flex-wrap: wrap;
|
|
}
|
|
.counter-item { font-size: var(--font-size-sm); }
|
|
.counter-value { font-weight: 700; font-size: var(--font-size-lg); }
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="match-page">
|
|
<a href="{{ url_for('admin.admin_gbp_audit') }}" class="back-link">← GBP Audit Dashboard</a>
|
|
|
|
<div class="match-header">
|
|
<h1>Dopasuj firmy do Google Maps</h1>
|
|
<p>{{ companies|length }} firm bez identyfikatora Google Place ID. Wyszukaj i potwierdz dopasowanie.</p>
|
|
</div>
|
|
|
|
<div class="counter">
|
|
<div class="counter-item">Pozostalo: <span class="counter-value" id="remainingCount">{{ companies|length }}</span></div>
|
|
<div class="counter-item">Dopasowano: <span class="counter-value" id="matchedCount" style="color: #16a34a;">0</span></div>
|
|
</div>
|
|
|
|
{% for c in companies %}
|
|
<div class="company-card" id="card-{{ c.id }}" data-company-id="{{ c.id }}">
|
|
<div class="company-header">
|
|
<div class="company-info">
|
|
<div class="company-name">{{ c.name }}</div>
|
|
<div class="company-city">{{ c.address_city or 'brak miasta' }}{% if c.website %} · {{ c.website }}{% endif %}</div>
|
|
</div>
|
|
<div>
|
|
<button class="btn-search" onclick="searchPlace({{ c.id }}, '{{ c.name|e }}')">Szukaj w Google</button>
|
|
<button class="btn-skip" onclick="skipCompany({{ c.id }})">Brak w Google</button>
|
|
</div>
|
|
</div>
|
|
<div class="search-results" id="results-{{ c.id }}"></div>
|
|
</div>
|
|
{% endfor %}
|
|
|
|
{% if not companies %}
|
|
<div style="text-align: center; padding: 60px 20px; color: var(--text-secondary);">
|
|
Wszystkie aktywne firmy maja przypisany Google Place ID.
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
var csrfToken = '{{ csrf_token() }}';
|
|
var matchedCount = 0;
|
|
var totalCompanies = {{ companies|length }};
|
|
|
|
function searchPlace(companyId, companyName) {
|
|
var resultsDiv = document.getElementById('results-' + companyId);
|
|
var btn = event.target;
|
|
btn.disabled = true;
|
|
btn.textContent = 'Szukam...';
|
|
|
|
resultsDiv.innerHTML = '<div class="search-loading">Szukam "' + companyName + '" w Google Maps...</div>';
|
|
resultsDiv.classList.add('visible');
|
|
|
|
var formData = new FormData();
|
|
formData.append('company_id', companyId);
|
|
formData.append('csrf_token', csrfToken);
|
|
|
|
fetch('{{ url_for("admin.admin_gbp_search_place") }}', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
btn.disabled = false;
|
|
btn.textContent = 'Szukaj w Google';
|
|
|
|
if (data.error) {
|
|
resultsDiv.innerHTML = '<div class="no-results">Blad: ' + data.error + '</div>';
|
|
return;
|
|
}
|
|
|
|
if (!data.results || data.results.length === 0) {
|
|
resultsDiv.innerHTML = '<div class="no-results">Nie znaleziono wynikow dla "' + data.query + '"</div>';
|
|
return;
|
|
}
|
|
|
|
var html = '';
|
|
data.results.forEach(function(p) {
|
|
var meta = [];
|
|
if (p.rating) meta.push('<span class="rating">' + p.rating + ' ★</span>');
|
|
if (p.reviews_count) meta.push(p.reviews_count + ' opinii');
|
|
if (p.types) meta.push(p.types);
|
|
|
|
html += '<div class="place-result">';
|
|
html += ' <div class="place-info">';
|
|
html += ' <div class="place-name">' + p.name + '</div>';
|
|
html += ' <div class="place-address">' + p.address + '</div>';
|
|
if (meta.length) html += ' <div class="place-meta">' + meta.join(' · ') + '</div>';
|
|
html += ' </div>';
|
|
html += ' <button class="btn-confirm" onclick="confirmPlace(' + companyId + ', \'' + p.place_id + '\', \'' + p.name.replace(/'/g, "\\'") + '\')">Potwierdz</button>';
|
|
html += '</div>';
|
|
});
|
|
|
|
resultsDiv.innerHTML = html;
|
|
})
|
|
.catch(function(err) {
|
|
btn.disabled = false;
|
|
btn.textContent = 'Szukaj w Google';
|
|
resultsDiv.innerHTML = '<div class="no-results">Blad polaczenia</div>';
|
|
});
|
|
}
|
|
|
|
function confirmPlace(companyId, placeId, googleName) {
|
|
var btn = event.target;
|
|
btn.disabled = true;
|
|
btn.textContent = 'Zapisuje...';
|
|
|
|
var formData = new FormData();
|
|
formData.append('company_id', companyId);
|
|
formData.append('place_id', placeId);
|
|
formData.append('google_name', googleName);
|
|
formData.append('csrf_token', csrfToken);
|
|
|
|
fetch('{{ url_for("admin.admin_gbp_confirm_place") }}', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (data.error) {
|
|
btn.disabled = false;
|
|
btn.textContent = 'Potwierdz';
|
|
alert('Blad: ' + data.error);
|
|
return;
|
|
}
|
|
|
|
var card = document.getElementById('card-' + companyId);
|
|
card.classList.add('matched');
|
|
var header = card.querySelector('.company-header');
|
|
var nameDiv = header.querySelector('.company-name');
|
|
nameDiv.innerHTML += ' <span class="match-badge">' + googleName + '</span>';
|
|
|
|
var resultsDiv = document.getElementById('results-' + companyId);
|
|
resultsDiv.classList.remove('visible');
|
|
|
|
var buttons = header.querySelectorAll('button');
|
|
buttons.forEach(function(b) { b.style.display = 'none'; });
|
|
|
|
matchedCount++;
|
|
document.getElementById('matchedCount').textContent = matchedCount;
|
|
document.getElementById('remainingCount').textContent = totalCompanies - matchedCount;
|
|
})
|
|
.catch(function(err) {
|
|
btn.disabled = false;
|
|
btn.textContent = 'Potwierdz';
|
|
alert('Blad polaczenia');
|
|
});
|
|
}
|
|
|
|
function skipCompany(companyId) {
|
|
var card = document.getElementById('card-' + companyId);
|
|
card.style.display = 'none';
|
|
totalCompanies--;
|
|
document.getElementById('remainingCount').textContent = totalCompanies - matchedCount;
|
|
}
|
|
{% endblock %}
|