Semplifica il grafico analytics: solo pageview e toggle nascondi.
Chiarisce che il totale è su tutte le pagine (filtri Da/A/Device), distinto dal dettaglio per path. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -58,42 +58,48 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if @trend.any? && (@trend_totals[:pageviews].positive? || @trend_totals[:clicks].positive? || @trend_totals[:moves].positive?) %>
|
<% if @trend.any? && @trend_totals[:pageviews].positive? %>
|
||||||
<section class="panel admin-analytics-trend">
|
<section class="panel admin-analytics-trend" id="admin-analytics-trend">
|
||||||
|
<div class="admin-analytics-trend__head">
|
||||||
|
<div>
|
||||||
<h3><%= t("admin.analytics.index.trend_title") %></h3>
|
<h3><%= t("admin.analytics.index.trend_title") %></h3>
|
||||||
<p class="muted admin-table-sub"><%= t("admin.analytics.index.trend_lead") %></p>
|
<p class="muted admin-table-sub"><%= t("admin.analytics.index.trend_lead") %></p>
|
||||||
|
</div>
|
||||||
|
<button type="button"
|
||||||
|
id="admin-analytics-trend-toggle"
|
||||||
|
class="admin-btn admin-btn--outline admin-btn--sm"
|
||||||
|
aria-controls="admin-analytics-trend-body"
|
||||||
|
aria-expanded="true"><%= t("admin.analytics.index.hide_chart") %></button>
|
||||||
|
</div>
|
||||||
|
<div id="admin-analytics-trend-body">
|
||||||
<div class="kpi-grid admin-analytics-trend__kpi">
|
<div class="kpi-grid admin-analytics-trend__kpi">
|
||||||
<div class="kpi">
|
<div class="kpi">
|
||||||
<div class="kpi-label"><%= t("admin.analytics.index.table.pageviews") %></div>
|
<div class="kpi-label"><%= t("admin.analytics.index.trend_total_label") %></div>
|
||||||
<div class="kpi-value"><%= @trend_totals[:pageviews] %></div>
|
<div class="kpi-value"><%= @trend_totals[:pageviews] %></div>
|
||||||
</div>
|
<div class="kpi-sub muted"><%= t("admin.analytics.index.trend_total_hint", from: l(@filters[:from]), to: l(@filters[:to])) %></div>
|
||||||
<div class="kpi">
|
|
||||||
<div class="kpi-label"><%= t("admin.analytics.index.table.clicks") %></div>
|
|
||||||
<div class="kpi-value"><%= @trend_totals[:clicks] %></div>
|
|
||||||
</div>
|
|
||||||
<div class="kpi">
|
|
||||||
<div class="kpi-label"><%= t("admin.analytics.index.table.moves") %></div>
|
|
||||||
<div class="kpi-value"><%= @trend_totals[:moves] %></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="chart-wrap chart-wrap--analytics">
|
<div class="chart-wrap chart-wrap--analytics">
|
||||||
<canvas id="chart-analytics-trend" aria-label="<%= t("admin.analytics.index.trend_title") %>"></canvas>
|
<canvas id="chart-analytics-trend" aria-label="<%= t("admin.analytics.index.trend_title") %>"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<script>
|
<script>
|
||||||
window.adminAnalyticsTrend = <%= raw @trend.to_json %>;
|
window.adminAnalyticsTrend = <%= raw @trend.to_json %>;
|
||||||
window.adminAnalyticsI18n = {
|
window.adminAnalyticsI18n = {
|
||||||
pageviews: <%= raw t("admin.analytics.index.table.pageviews").to_json %>,
|
pageviews: <%= raw t("admin.analytics.index.table.pageviews").to_json %>,
|
||||||
clicks: <%= raw t("admin.analytics.index.table.clicks").to_json %>,
|
hideChart: <%= raw t("admin.analytics.index.hide_chart").to_json %>,
|
||||||
moves: <%= raw t("admin.analytics.index.table.moves").to_json %>
|
showChart: <%= raw t("admin.analytics.index.show_chart").to_json %>
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js" crossorigin="anonymous"></script>
|
||||||
<script src="/admin-analytics.js?v=1" defer></script>
|
<script src="/admin-analytics.js?v=2" defer></script>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<% if @pages.any? %>
|
<% if @pages.any? %>
|
||||||
|
<h3 class="admin-analytics-pages-title"><%= t("admin.analytics.index.pages_title") %></h3>
|
||||||
|
<p class="muted admin-table-sub"><%= t("admin.analytics.index.pages_lead") %></p>
|
||||||
<div class="admin-table-wrap">
|
<div class="admin-table-wrap">
|
||||||
<table class="admin-table">
|
<table class="admin-table">
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="robots" content="noindex, nofollow">
|
<meta name="robots" content="noindex, nofollow">
|
||||||
<%= csrf_meta_tags %>
|
<%= csrf_meta_tags %>
|
||||||
<link rel="stylesheet" href="/admin.css?v=17">
|
<link rel="stylesheet" href="/admin.css?v=18">
|
||||||
<%= yield :head %>
|
<%= yield :head %>
|
||||||
<% if content_for?(:replay_archive_styles) %>
|
<% if content_for?(:replay_archive_styles) %>
|
||||||
<link rel="stylesheet" href="/marketing.css?v=42">
|
<link rel="stylesheet" href="/marketing.css?v=42">
|
||||||
|
|||||||
@@ -427,8 +427,14 @@ de:
|
|||||||
lead: Aggregierte First-Party-Heatmaps (Bewegung/Klick) und Scrolltiefe, nur mit Statistik-Einwilligung. Keine personenbezogenen Daten.
|
lead: Aggregierte First-Party-Heatmaps (Bewegung/Klick) und Scrolltiefe, nur mit Statistik-Einwilligung. Keine personenbezogenen Daten.
|
||||||
none: Keine Daten im gewählten Zeitraum.
|
none: Keine Daten im gewählten Zeitraum.
|
||||||
heatmap: Heatmap
|
heatmap: Heatmap
|
||||||
trend_title: Verlauf über die Zeit
|
trend_title: Pageview-Verlauf
|
||||||
trend_lead: Tägliche Pageviews, Klicks und Bewegungen im gefilterten Zeitraum.
|
trend_lead: Gesamte Pageviews aller Seiten, Tag für Tag. Dieselben Filter Von / Bis / Gerät. Nicht mit einer Tabellenzeile vergleichbar (die nach Pfad trennt).
|
||||||
|
trend_total_label: Pageviews gesamt (alle Seiten)
|
||||||
|
trend_total_hint: "%{from} → %{to}"
|
||||||
|
hide_chart: Diagramm ausblenden
|
||||||
|
show_chart: Diagramm anzeigen
|
||||||
|
pages_title: Aufschlüsselung nach Seite
|
||||||
|
pages_lead: Pageviews und Engagement im gefilterten Zeitraum, nach Pfad. Die Summe der Pageview-Zeilen entspricht dem Diagramm-Total.
|
||||||
table:
|
table:
|
||||||
path: Seite
|
path: Seite
|
||||||
pageviews: Pageviews
|
pageviews: Pageviews
|
||||||
|
|||||||
@@ -427,8 +427,14 @@ en:
|
|||||||
lead: Aggregated first-party move/click heatmaps and scroll depth, only with analytics consent. No personal data.
|
lead: Aggregated first-party move/click heatmaps and scroll depth, only with analytics consent. No personal data.
|
||||||
none: No data in the selected period.
|
none: No data in the selected period.
|
||||||
heatmap: Heatmap
|
heatmap: Heatmap
|
||||||
trend_title: Trend over time
|
trend_title: Pageview trend
|
||||||
trend_lead: Daily pageviews, clicks and moves for the filtered period.
|
trend_lead: Total pageviews across all pages, day by day. Uses the same From / To / Device filters. Not comparable to a single table row (which splits by path).
|
||||||
|
trend_total_label: Total pageviews (all pages)
|
||||||
|
trend_total_hint: "%{from} → %{to}"
|
||||||
|
hide_chart: Hide chart
|
||||||
|
show_chart: Show chart
|
||||||
|
pages_title: Breakdown by page
|
||||||
|
pages_lead: Pageviews and engagement for the filtered period, split by path. The sum of pageview rows matches the chart total.
|
||||||
table:
|
table:
|
||||||
path: Page
|
path: Page
|
||||||
pageviews: Pageviews
|
pageviews: Pageviews
|
||||||
|
|||||||
@@ -427,8 +427,14 @@ es:
|
|||||||
lead: Heatmaps de movimientos/clics y scroll agregados (first-party), solo con consentimiento estadístico. Sin datos personales.
|
lead: Heatmaps de movimientos/clics y scroll agregados (first-party), solo con consentimiento estadístico. Sin datos personales.
|
||||||
none: No hay datos en el periodo seleccionado.
|
none: No hay datos en el periodo seleccionado.
|
||||||
heatmap: Heatmap
|
heatmap: Heatmap
|
||||||
trend_title: Evolución en el tiempo
|
trend_title: Evolución de pageviews
|
||||||
trend_lead: Pageviews, clics y movimientos día a día en el periodo filtrado.
|
trend_lead: Total de pageviews de todas las páginas, día a día. Usa los mismos filtros Desde / Hasta / Device. No es comparable con una fila de la tabla (que parte por path).
|
||||||
|
trend_total_label: Pageviews totales (todas las páginas)
|
||||||
|
trend_total_hint: "%{from} → %{to}"
|
||||||
|
hide_chart: Ocultar gráfico
|
||||||
|
show_chart: Mostrar gráfico
|
||||||
|
pages_title: Detalle por página
|
||||||
|
pages_lead: Pageviews y engagement en el periodo filtrado, por path. La suma de las filas coincide con el total del gráfico.
|
||||||
table:
|
table:
|
||||||
path: Página
|
path: Página
|
||||||
pageviews: Pageviews
|
pageviews: Pageviews
|
||||||
|
|||||||
@@ -427,8 +427,14 @@ fr:
|
|||||||
lead: Heatmaps mouvements/clics et scroll agrégés (first-party), uniquement avec consentement statistiques. Aucune donnée personnelle.
|
lead: Heatmaps mouvements/clics et scroll agrégés (first-party), uniquement avec consentement statistiques. Aucune donnée personnelle.
|
||||||
none: Aucune donnée sur la période sélectionnée.
|
none: Aucune donnée sur la période sélectionnée.
|
||||||
heatmap: Heatmap
|
heatmap: Heatmap
|
||||||
trend_title: Évolution dans le temps
|
trend_title: Évolution des pages vues
|
||||||
trend_lead: Pages vues, clics et mouvements par jour sur la période filtrée.
|
trend_lead: Total des pages vues sur toutes les pages, jour par jour. Mêmes filtres De / À / Appareil. Non comparable à une ligne du tableau (découpée par chemin).
|
||||||
|
trend_total_label: Pages vues totales (toutes les pages)
|
||||||
|
trend_total_hint: "%{from} → %{to}"
|
||||||
|
hide_chart: Masquer le graphique
|
||||||
|
show_chart: Afficher le graphique
|
||||||
|
pages_title: Détail par page
|
||||||
|
pages_lead: Pages vues et engagement sur la période filtrée, par chemin. La somme des lignes égale le total du graphique.
|
||||||
table:
|
table:
|
||||||
path: Page
|
path: Page
|
||||||
pageviews: Pages vues
|
pageviews: Pages vues
|
||||||
|
|||||||
@@ -448,8 +448,14 @@ it:
|
|||||||
lead: Heatmap movimenti/click e scroll aggregati (first-party), solo con consenso statistico. Nessun dato personale.
|
lead: Heatmap movimenti/click e scroll aggregati (first-party), solo con consenso statistico. Nessun dato personale.
|
||||||
none: Nessun dato nel periodo selezionato.
|
none: Nessun dato nel periodo selezionato.
|
||||||
heatmap: Heatmap
|
heatmap: Heatmap
|
||||||
trend_title: Andamento nel tempo
|
trend_title: Andamento pageview
|
||||||
trend_lead: Pageview, click e movimenti giorno per giorno nel periodo filtrato.
|
trend_lead: Totale pageview di tutte le pagine, giorno per giorno. Usa gli stessi filtri Da / A / Device. Non è confrontabile con una singola riga della tabella (che spezza per path).
|
||||||
|
trend_total_label: Pageview totali (tutte le pagine)
|
||||||
|
trend_total_hint: "%{from} → %{to}"
|
||||||
|
hide_chart: Nascondi grafico
|
||||||
|
show_chart: Mostra grafico
|
||||||
|
pages_title: Dettaglio per pagina
|
||||||
|
pages_lead: Pageview e engagement nel periodo filtrato, spezzati per path. La somma delle pageview delle righe coincide col totale del grafico.
|
||||||
table:
|
table:
|
||||||
path: Pagina
|
path: Pagina
|
||||||
pageviews: Pageview
|
pageviews: Pageview
|
||||||
|
|||||||
@@ -1,7 +1,40 @@
|
|||||||
(function () {
|
(function () {
|
||||||
|
var STORAGE_KEY = "mltv_admin_analytics_chart_hidden";
|
||||||
var trend = window.adminAnalyticsTrend || [];
|
var trend = window.adminAnalyticsTrend || [];
|
||||||
var i18n = window.adminAnalyticsI18n || {};
|
var i18n = window.adminAnalyticsI18n || {};
|
||||||
|
var panel = document.getElementById("admin-analytics-trend");
|
||||||
var canvas = document.getElementById("chart-analytics-trend");
|
var canvas = document.getElementById("chart-analytics-trend");
|
||||||
|
var toggle = document.getElementById("admin-analytics-trend-toggle");
|
||||||
|
var body = document.getElementById("admin-analytics-trend-body");
|
||||||
|
|
||||||
|
function setHidden(hidden) {
|
||||||
|
if (!panel || !body || !toggle) return;
|
||||||
|
body.hidden = hidden;
|
||||||
|
panel.classList.toggle("is-collapsed", hidden);
|
||||||
|
toggle.setAttribute("aria-expanded", hidden ? "false" : "true");
|
||||||
|
toggle.textContent = hidden
|
||||||
|
? (i18n.showChart || "Mostra grafico")
|
||||||
|
: (i18n.hideChart || "Nascondi grafico");
|
||||||
|
try {
|
||||||
|
localStorage.setItem(STORAGE_KEY, hidden ? "1" : "0");
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
function initiallyHidden() {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem(STORAGE_KEY) === "1";
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toggle && body) {
|
||||||
|
setHidden(initiallyHidden());
|
||||||
|
toggle.addEventListener("click", function () {
|
||||||
|
setHidden(!body.hidden);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!canvas || !trend.length || typeof Chart === "undefined") return;
|
if (!canvas || !trend.length || typeof Chart === "undefined") return;
|
||||||
|
|
||||||
function dayLabel(dayStr) {
|
function dayLabel(dayStr) {
|
||||||
@@ -11,14 +44,10 @@
|
|||||||
return d.toLocaleDateString([], { day: "2-digit", month: "short" });
|
return d.toLocaleDateString([], { day: "2-digit", month: "short" });
|
||||||
}
|
}
|
||||||
|
|
||||||
var labels = trend.map(function (row) {
|
|
||||||
return dayLabel(row.day);
|
|
||||||
});
|
|
||||||
|
|
||||||
new Chart(canvas, {
|
new Chart(canvas, {
|
||||||
type: "line",
|
type: "line",
|
||||||
data: {
|
data: {
|
||||||
labels: labels,
|
labels: trend.map(function (row) { return dayLabel(row.day); }),
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: i18n.pageviews || "Pageviews",
|
label: i18n.pageviews || "Pageviews",
|
||||||
@@ -29,27 +58,6 @@
|
|||||||
tension: 0.25,
|
tension: 0.25,
|
||||||
pointRadius: 3,
|
pointRadius: 3,
|
||||||
pointHoverRadius: 5
|
pointHoverRadius: 5
|
||||||
},
|
|
||||||
{
|
|
||||||
label: i18n.clicks || "Clicks",
|
|
||||||
data: trend.map(function (r) { return r.clicks || 0; }),
|
|
||||||
borderColor: "rgba(66, 165, 245, 0.95)",
|
|
||||||
backgroundColor: "transparent",
|
|
||||||
fill: false,
|
|
||||||
tension: 0.25,
|
|
||||||
pointRadius: 2,
|
|
||||||
pointHoverRadius: 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: i18n.moves || "Moves",
|
|
||||||
data: trend.map(function (r) { return r.moves || 0; }),
|
|
||||||
borderColor: "rgba(255, 193, 7, 0.85)",
|
|
||||||
backgroundColor: "transparent",
|
|
||||||
fill: false,
|
|
||||||
tension: 0.25,
|
|
||||||
pointRadius: 2,
|
|
||||||
pointHoverRadius: 4,
|
|
||||||
borderDash: [4, 3]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -74,7 +82,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { labels: { color: "#ccc", boxWidth: 12 } }
|
legend: { display: false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -178,10 +178,31 @@ body.admin-body {
|
|||||||
margin-top: 0.75rem;
|
margin-top: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-analytics-trend__head {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-analytics-trend__head h3 {
|
||||||
|
margin: 0 0 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
.admin-analytics-trend__kpi {
|
.admin-analytics-trend__kpi {
|
||||||
margin: 0.75rem 0 0;
|
margin: 0.75rem 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-analytics-trend.is-collapsed .admin-analytics-trend__head {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-analytics-pages-title {
|
||||||
|
margin: 0 0 0.35rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.admin-panels {
|
.admin-panels {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1.4fr 1fr;
|
grid-template-columns: 1.4fr 1fr;
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ RSpec.describe "Admin analytics", type: :request do
|
|||||||
|
|
||||||
before do
|
before do
|
||||||
post admin_login_path, params: { username: admin.username, password: "Password123" }
|
post admin_login_path, params: { username: admin.username, password: "Password123" }
|
||||||
|
AnalyticsPageStat.delete_all
|
||||||
|
AnalyticsPageCell.delete_all
|
||||||
AnalyticsPageStat.create!(
|
AnalyticsPageStat.create!(
|
||||||
day: Time.zone.today,
|
day: Time.zone.today,
|
||||||
page_path: "/prezzi",
|
page_path: "/prezzi",
|
||||||
@@ -35,9 +37,12 @@ RSpec.describe "Admin analytics", type: :request do
|
|||||||
expect(response.body).to include("40")
|
expect(response.body).to include("40")
|
||||||
expect(response.body).to include("chart-analytics-trend")
|
expect(response.body).to include("chart-analytics-trend")
|
||||||
expect(response.body).to include("adminAnalyticsTrend")
|
expect(response.body).to include("adminAnalyticsTrend")
|
||||||
|
expect(response.body).to include(I18n.t("admin.analytics.index.hide_chart"))
|
||||||
|
expect(response.body).to include("hideChart")
|
||||||
|
expect(response.body).not_to include("clicks:")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "mostra il trend anche su più giorni" do
|
it "il grafico rispetta i filtri data e aggrega pageview di tutte le pagine" do
|
||||||
AnalyticsPageStat.create!(
|
AnalyticsPageStat.create!(
|
||||||
day: 1.day.ago.to_date,
|
day: 1.day.ago.to_date,
|
||||||
page_path: "/prezzi",
|
page_path: "/prezzi",
|
||||||
@@ -47,13 +52,25 @@ RSpec.describe "Admin analytics", type: :request do
|
|||||||
scroll_sum_pct: 0,
|
scroll_sum_pct: 0,
|
||||||
max_scroll_pct: 0
|
max_scroll_pct: 0
|
||||||
)
|
)
|
||||||
|
AnalyticsPageStat.create!(
|
||||||
|
day: Time.zone.today,
|
||||||
|
page_path: "/altro",
|
||||||
|
device: "desktop",
|
||||||
|
pageview_count: 7,
|
||||||
|
scroll_samples: 0,
|
||||||
|
scroll_sum_pct: 0,
|
||||||
|
max_scroll_pct: 0
|
||||||
|
)
|
||||||
|
|
||||||
get admin_analytics_path, params: { from: 2.days.ago.to_date, to: Time.zone.today }
|
get admin_analytics_path, params: { from: 2.days.ago.to_date, to: Time.zone.today }
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
|
expect(response.body).to include(I18n.t("admin.analytics.index.trend_total_label"))
|
||||||
|
expect(response.body).to include(">24<")
|
||||||
expect(response.body).to include("\"pageviews\":5")
|
expect(response.body).to include("\"pageviews\":5")
|
||||||
expect(response.body).to include("\"pageviews\":12")
|
expect(response.body).to include("\"pageviews\":19")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
it "mostra la heatmap di una pagina con tab dispositivo" do
|
it "mostra la heatmap di una pagina con tab dispositivo" do
|
||||||
get admin_analytics_page_path, params: { page_path: "/prezzi" }
|
get admin_analytics_page_path, params: { page_path: "/prezzi" }
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
|
|||||||
Reference in New Issue
Block a user