niki-til

<h1 class="admin-page-title">メトリクス</h1> <p class="admin-page-meta">SEO と GEO の週次トレンド。</p>

<script id="seo-data" type="application/json">{{ site.data.seo | jsonify }}</script> <script id="geo-data" type="application/json">{{ site.data.geo | jsonify }}</script>

<h2 class="admin-section-title">SEO サマリー(7日 vs 30日)</h2>

{% assign s7 = site.data.seo.summary["7d"] %} {% assign s30 = site.data.seo.summary["30d"] %}

<div class="kpi-grid" style="margin-bottom:2rem;"> <div class="kpi-card"> <p class="kpi-label">表示回数 7d</p> <p class="kpi-value">{{ s7.impressions | default: "0" }}</p> <p class="kpi-sub">30d: {{ s30.impressions | default: "0" }}</p> </div> <div class="kpi-card"> <p class="kpi-label">クリック数 7d</p> <p class="kpi-value">{{ s7.clicks | default: "0" }}</p> <p class="kpi-sub">30d: {{ s30.clicks | default: "0" }}</p> </div> <div class="kpi-card"> <p class="kpi-label">平均順位 7d</p> <p class="kpi-value">{% if s7.avg_position and s7.avg_position != 0 %}{{ s7.avg_position | round: 1 }}{% else %}—{% endif %}</p> <p class="kpi-sub">30d: {% if s30.avg_position and s30.avg_position != 0 %}{{ s30.avg_position | round: 1 }}{% else %}—{% endif %}</p> </div> <div class="kpi-card"> <p class="kpi-label">CTR 7d</p> <p class="kpi-value">{% if s7.avg_ctr and s7.avg_ctr != 0 %}{{ s7.avg_ctr | times: 100 | round: 2 }}%{% else %}—{% endif %}</p> <p class="kpi-sub">30d: {% if s30.avg_ctr and s30.avg_ctr != 0 %}{{ s30.avg_ctr | times: 100 | round: 2 }}%{% else %}—{% endif %}</p> </div> </div>

<h2 class="admin-section-title">GEO サマリー(7日 vs 30日)</h2>

{% assign g7 = site.data.geo.summary["7d"] %} {% assign g30 = site.data.geo.summary["30d"] %}

<div class="kpi-grid" style="margin-bottom:2rem;"> <div class="kpi-card"> <p class="kpi-label">総セッション 7d</p> <p class="kpi-value">{{ g7.total_sessions | default: "0" }}</p> <p class="kpi-sub">30d: {{ g30.total_sessions | default: "0" }}</p> </div> <div class="kpi-card"> <p class="kpi-label">AI 流入 7d</p> <p class="kpi-value">{{ g7.ai_search_sessions | default: "0" }}</p> <p class="kpi-sub">30d: {{ g30.ai_search_sessions | default: "0" }}</p> </div> <div class="kpi-card"> <p class="kpi-label">AI 比率 7d</p> <p class="kpi-value">{{ g7.ai_share_pct | default: "0" }}%</p> <p class="kpi-sub">30d: {{ g30.ai_share_pct | default: "0" }}%</p> </div> <div class="kpi-card"> <p class="kpi-label">通常検索 7d</p> <p class="kpi-value">{{ g7.organic_search_sessions | default: "0" }}</p> <p class="kpi-sub">30d: {{ g30.organic_search_sessions | default: "0" }}</p> </div> </div>

<h2 class="admin-section-title">日次インプレッション推移(GSC)</h2> <div class="chart-wrap"> <p class="chart-title">表示回数 & クリック数(日次)</p> <canvas id="chart-seo-daily" aria-label="SEO日次チャート" role="img"></canvas> </div>

<h2 class="admin-section-title">日次セッション推移(GA4)</h2> <div class="chart-wrap"> <p class="chart-title">セッション種別 — 現在日次データは geo.json には含まれません</p> <canvas id="chart-geo-sessions" aria-label="GEOセッション推移チャート" role="img"></canvas> </div>

<script defer> (function() { var rawSeo = document.getElementById('seo-data'); var rawGeo = document.getElementById('geo-data'); if (!rawSeo) return;

var seo, geo; try { seo = JSON.parse(rawSeo.textContent); geo = rawGeo ? JSON.parse(rawGeo.textContent) : {}; } catch(e) { return; }

var seoDaily = seo.site_daily || [];

var baseChartOpts = { responsive: true, maintainAspectRatio: true, interaction: { mode: 'index', intersect: false }, plugins: { legend: { position: 'bottom', labels: { font: { family: 'Inter', size: 11 } } } }, scales: { x: { ticks: { font: { family: 'Inter', size: 10 }, maxRotation: 45 }, grid: { color: 'rgba(0,0,0,0.04)' } }, y: { ticks: { font: { family: 'Inter', size: 11 } }, grid: { color: 'rgba(0,0,0,0.04)' }, beginAtZero: true } } };

function whenChart(fn) { if (typeof Chart !== 'undefined') { fn(); return; } var t = setInterval(function() { if (typeof Chart !== 'undefined') { clearInterval(t); fn(); } }, 100); }

whenChart(function() { // SEO daily var seoEl = document.getElementById('chart-seo-daily'); if (seoEl && seoDaily.length) { new Chart(seoEl, { type: 'line', data: { labels: seoDaily.map(function(d){ return d.date; }), datasets: [ { label: '表示回数', data: seoDaily.map(function(d){ return d.impressions||0; }), borderColor: '#A1BAEC', backgroundColor: 'rgba(161,186,236,0.1)', tension: 0.3, pointRadius: 2 }, { label: 'クリック数', data: seoDaily.map(function(d){ return d.clicks||0; }), borderColor: '#B22E20', backgroundColor: 'rgba(178,46,32,0.07)', tension: 0.3, pointRadius: 2 } ] }, options: baseChartOpts }); } else if (seoEl) { seoEl.parentElement.insertAdjacentHTML('beforeend', '<p style="font-size:12px;color:var(--color-fg-muted);">日次データ蓄積待ち。</p>'); }

// GEO — geo.json に daily は含まれないためプレースホルダ var geoEl = document.getElementById('chart-geo-sessions'); if (geoEl) { geoEl.parentElement.insertAdjacentHTML('beforeend', '<p style="font-size:12px;color:var(--color-fg-muted);">geo.json は日次データを含みません。GA4 API に daily breakdown を追加すると表示されます。</p>'); geoEl.style.display = 'none'; } }); })(); </script>