Files
backPRO/templates/sites/seo.php
Jacek Pyziak b2aead1fbe feat: Integrate DataForSEO for indexed pages tracking
- Updated CRON documentation to include DataForSEO metrics synchronization.
- Enhanced SettingsController to manage DataForSEO API credentials and settings.
- Modified SiteController to handle DataForSEO domain input.
- Updated Site model to accommodate DataForSEO data handling.
- Added methods in SiteSeoMetric model for DataForSEO data retrieval and validation.
- Implemented SiteSeoSyncService to synchronize SEO metrics from both SEMSTORM and DataForSEO.
- Enhanced dashboard templates to display indexed pages data.
- Updated settings and site creation/edit templates to include DataForSEO fields.
- Created migration for adding DataForSEO related columns in the database.
- Developed DataForSeoService to fetch indexed pages count from DataForSEO API.
2026-02-21 11:41:17 +01:00

201 lines
8.5 KiB
PHP

<div class="d-flex justify-content-between align-items-center mb-4">
<h2>SEO Panel: <?= htmlspecialchars((string) ($site['url'] ?? '')) ?></h2>
<div class="d-flex gap-2">
<a href="/sites/<?= (int) $site['id'] ?>/dashboard" class="btn btn-outline-dark">
<i class="bi bi-sliders me-1"></i>WP Dashboard
</a>
<a href="/sites/<?= (int) $site['id'] ?>/edit" class="btn btn-outline-secondary">
<i class="bi bi-pencil me-1"></i>Edytuj strone
</a>
<a href="/sites" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-1"></i>Lista stron
</a>
</div>
</div>
<div class="row g-4">
<div class="col-lg-8">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Widocznosc SEO (SEMSTORM)</h5>
<form method="post" action="/sites/<?= (int) $site['id'] ?>/seo/sync" data-confirm="Pobrac i nadpisac dane SEO dla biezacego miesiaca?">
<button type="submit" class="btn btn-sm btn-outline-primary">
<i class="bi bi-arrow-repeat me-1"></i>Synchronizuj teraz
</button>
</form>
</div>
<div class="card-body">
<div class="position-relative" style="height: 340px;">
<canvas id="seoVisibilityChart"></canvas>
</div>
</div>
</div>
<div class="card mt-4 border-secondary-subtle">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0">Zaindeksowane strony (DataForSEO)</h6>
</div>
<div class="card-body">
<?php if (!empty($seoLatest)): ?>
<div class="display-6 fw-semibold mb-1"><?= (int) ($seoLatest['indexed_pages'] ?? 0) ?></div>
<p class="small text-muted mb-0">
Ostatnia aktualizacja miesieczna:
<?= htmlspecialchars(date('m.Y', strtotime((string) $seoLatest['metric_month']))) ?>
</p>
<?php else: ?>
<p class="text-muted small mb-0">Brak danych o indeksacji. Uzyj przycisku "Synchronizuj teraz".</p>
<?php endif; ?>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">Aktualne metryki</h5>
</div>
<div class="card-body">
<?php if (!empty($seoLatest)): ?>
<div class="row g-2 mb-3">
<div class="col-6">
<div class="border rounded p-2">
<div class="small text-muted">TOP3</div>
<div class="fw-semibold"><?= (int) $seoLatest['top3'] ?></div>
</div>
</div>
<div class="col-6">
<div class="border rounded p-2">
<div class="small text-muted">TOP10</div>
<div class="fw-semibold"><?= (int) $seoLatest['top10'] ?></div>
</div>
</div>
<div class="col-6">
<div class="border rounded p-2">
<div class="small text-muted">TOP20</div>
<div class="fw-semibold"><?= (int) $seoLatest['top20'] ?></div>
</div>
</div>
<div class="col-6">
<div class="border rounded p-2">
<div class="small text-muted">TOP50</div>
<div class="fw-semibold"><?= (int) $seoLatest['top50'] ?></div>
</div>
</div>
</div>
<p class="small mb-2"><strong>Ruch:</strong> <?= (int) $seoLatest['traffic'] ?></p>
<p class="small mb-2"><strong>Zaindeksowane strony:</strong> <?= (int) ($seoLatest['indexed_pages'] ?? 0) ?></p>
<p class="small text-muted mb-0">
Ostatni zapis: <?= htmlspecialchars(date('d.m.Y H:i', strtotime((string) $seoLatest['updated_at']))) ?><br>
Miesiac: <?= htmlspecialchars(date('m.Y', strtotime((string) $seoLatest['metric_month']))) ?>
</p>
<?php else: ?>
<p class="text-muted small mb-0">Brak zapisanych danych SEO. Uzyj przycisku "Synchronizuj teraz".</p>
<?php endif; ?>
</div>
</div>
<div class="card border-info">
<div class="card-header bg-info-subtle">
<h5 class="mb-0">Kolejne zrodla (plan)</h5>
</div>
<div class="card-body">
<p class="small text-muted mb-0">Ten panel jest przygotowany pod dodatkowe integracje SEO (np. GSC, Ahrefs, Senuto) bez mieszania z ustawieniami WordPress.</p>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.4/dist/chart.umd.min.js"></script>
<script>
(function () {
var seoMetrics = <?= json_encode($seoMetrics ?? [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
var seoCanvas = document.getElementById('seoVisibilityChart');
if (!seoCanvas || !window.Chart) {
return;
}
if (!Array.isArray(seoMetrics) || seoMetrics.length === 0) {
seoCanvas.style.display = 'none';
return;
}
var labels = seoMetrics.map(function (item) {
var date = new Date(item.metric_month);
if (isNaN(date.getTime())) return item.metric_month;
var month = String(date.getMonth() + 1).padStart(2, '0');
return month + '.' + date.getFullYear();
});
new Chart(seoCanvas, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'TOP3',
data: seoMetrics.map(function (item) { return parseInt(item.top3 || 0, 10); }),
borderColor: '#0d6efd',
backgroundColor: 'rgba(13,110,253,0.15)',
tension: 0.25,
yAxisID: 'y'
},
{
label: 'TOP10',
data: seoMetrics.map(function (item) { return parseInt(item.top10 || 0, 10); }),
borderColor: '#20c997',
backgroundColor: 'rgba(32,201,151,0.15)',
tension: 0.25,
yAxisID: 'y'
},
{
label: 'TOP20',
data: seoMetrics.map(function (item) { return parseInt(item.top20 || 0, 10); }),
borderColor: '#fd7e14',
backgroundColor: 'rgba(253,126,20,0.15)',
tension: 0.25,
yAxisID: 'y'
},
{
label: 'TOP50',
data: seoMetrics.map(function (item) { return parseInt(item.top50 || 0, 10); }),
borderColor: '#dc3545',
backgroundColor: 'rgba(220,53,69,0.15)',
tension: 0.25,
yAxisID: 'y'
},
{
label: 'Ruch',
data: seoMetrics.map(function (item) { return parseInt(item.traffic || 0, 10); }),
borderColor: '#6f42c1',
backgroundColor: 'rgba(111,66,193,0.15)',
tension: 0.25,
yAxisID: 'yTraffic'
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: {
mode: 'index',
intersect: false
},
scales: {
y: {
beginAtZero: true,
position: 'left',
title: { display: true, text: 'TOP' }
},
yTraffic: {
beginAtZero: true,
position: 'right',
grid: { drawOnChartArea: false },
title: { display: true, text: 'Ruch' }
}
}
}
});
})();
</script>