Separa heatmap mobile/desktop, opt-out analytics per operatori, dashboard costi con KPI e trend mensili. Co-authored-by: Cursor <cursoragent@cursor.com>
167 lines
6.0 KiB
ERB
167 lines
6.0 KiB
ERB
<% content_for :body_class, "admin-body" %>
|
|
|
|
<div class="admin-page-head admin-session-head">
|
|
<div>
|
|
<p class="muted admin-page-sub">
|
|
<%= link_to t("admin.analytics.show.back"), admin_analytics_path(from: @filters[:from], to: @filters[:to]) %>
|
|
</p>
|
|
<h2 class="admin-page-title"><%= t("admin.analytics.show.title") %></h2>
|
|
<p><code class="admin-mono"><%= @page_path %></code></p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel admin-sessions-filters">
|
|
<p class="admin-filter-field" style="margin-bottom: 0.75rem;">
|
|
<span><%= t("admin.analytics.show.device_tabs_label") %></span>
|
|
</p>
|
|
<div class="admin-locale-tabs" role="tablist">
|
|
<% AnalyticsEvent::DEVICES.each do |device| %>
|
|
<% stats = @device_tab_stats[device] %>
|
|
<% active = @filters[:device] == device %>
|
|
<%= link_to admin_analytics_page_path(
|
|
page_path: @page_path,
|
|
from: @filters[:from],
|
|
to: @filters[:to],
|
|
device: device,
|
|
layer: @filters[:layer]
|
|
),
|
|
class: "admin-locale-tab #{'is-active' if active}",
|
|
role: "tab",
|
|
"aria-selected": active do %>
|
|
<%= t("admin.analytics.devices.#{device}") %>
|
|
<span class="admin-device-tab-count"><%= stats[:pageviews] %></span>
|
|
<% end %>
|
|
<% end %>
|
|
</div>
|
|
|
|
<%= form_with url: admin_analytics_page_path, method: :get, local: true, class: "admin-filter-form" do %>
|
|
<%= hidden_field_tag :page_path, @page_path %>
|
|
<%= hidden_field_tag :device, @filters[:device] %>
|
|
<div class="admin-filter-grid">
|
|
<label class="admin-filter-field">
|
|
<span><%= t("admin.analytics.filters.from") %></span>
|
|
<%= date_field_tag :from, @filters[:from] %>
|
|
</label>
|
|
<label class="admin-filter-field">
|
|
<span><%= t("admin.analytics.filters.to") %></span>
|
|
<%= date_field_tag :to, @filters[:to] %>
|
|
</label>
|
|
<label class="admin-filter-field">
|
|
<span><%= t("admin.analytics.filters.layer") %></span>
|
|
<%= select_tag :layer,
|
|
options_for_select(
|
|
[
|
|
[t("admin.analytics.filters.layer_move"), "move"],
|
|
[t("admin.analytics.filters.layer_click"), "click"]
|
|
],
|
|
@filters[:layer]
|
|
) %>
|
|
</label>
|
|
</div>
|
|
<div class="admin-filter-actions">
|
|
<%= submit_tag t("admin.analytics.filters.apply"), class: "admin-btn admin-btn--primary admin-btn--sm" %>
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
|
|
<div class="admin-session-grid">
|
|
<section class="panel">
|
|
<h3><%= t("admin.analytics.show.summary") %></h3>
|
|
<dl class="admin-kv">
|
|
<div>
|
|
<dt><%= t("admin.analytics.index.table.pageviews") %></dt>
|
|
<dd><%= @pageviews %></dd>
|
|
</div>
|
|
<div>
|
|
<dt><%= t("admin.analytics.index.table.moves") %></dt>
|
|
<dd><%= @move_total %></dd>
|
|
</div>
|
|
<div>
|
|
<dt><%= t("admin.analytics.index.table.clicks") %></dt>
|
|
<dd><%= @click_total %></dd>
|
|
</div>
|
|
<div>
|
|
<dt><%= t("admin.analytics.index.table.avg_scroll") %></dt>
|
|
<dd><%= @avg_scroll %>%</dd>
|
|
</div>
|
|
<div>
|
|
<dt><%= t("admin.analytics.index.table.max_scroll") %></dt>
|
|
<dd><%= @max_scroll %>%</dd>
|
|
</div>
|
|
</dl>
|
|
</section>
|
|
|
|
<section class="panel" style="grid-column: span 2;">
|
|
<h3><%= t("admin.analytics.show.scroll_depth") %></h3>
|
|
<div class="admin-scroll-bar" aria-hidden="true">
|
|
<div class="admin-scroll-bar__avg" style="width: <%= @avg_scroll %>%"></div>
|
|
<div class="admin-scroll-bar__max" style="left: <%= @max_scroll %>%"></div>
|
|
</div>
|
|
<p class="muted admin-table-sub">
|
|
<%= t("admin.analytics.show.scroll_hint", avg: @avg_scroll, max: @max_scroll) %>
|
|
</p>
|
|
</section>
|
|
</div>
|
|
|
|
<section class="panel">
|
|
<h3>
|
|
<%= @filters[:layer] == "click" ? t("admin.analytics.show.heatmap_clicks") : t("admin.analytics.show.heatmap_moves") %>
|
|
</h3>
|
|
<p class="muted admin-table-sub">
|
|
<%= t(
|
|
"admin.analytics.show.heatmap_hint",
|
|
device: t("admin.analytics.devices.#{@filters[:device]}")
|
|
) %>
|
|
</p>
|
|
|
|
<% if @cells.any? %>
|
|
<div class="admin-heatmap-stage" id="admin-heatmap-stage">
|
|
<% if @snapshot&.image&.attached? %>
|
|
<div
|
|
class="admin-heatmap-scaler"
|
|
id="admin-heatmap-scaler"
|
|
data-design-width="<%= [@snapshot.width, 1].max %>"
|
|
data-design-height="<%= [@snapshot.height, 1].max %>"
|
|
>
|
|
<%= image_tag url_for(@snapshot.image),
|
|
class: "admin-heatmap-shot",
|
|
id: "admin-heatmap-shot",
|
|
alt: t("admin.analytics.show.preview_title") %>
|
|
<canvas
|
|
id="admin-heatmap"
|
|
class="admin-heatmap-overlay"
|
|
data-grid="<%= @grid %>"
|
|
data-max="<%= [@max_weight, 1].max %>"
|
|
data-cells="<%= @cells.map { |(x, y), c| { x: x, y: y, c: c } }.to_json %>"
|
|
></canvas>
|
|
</div>
|
|
<p class="muted admin-table-sub" style="margin: 0.75rem 1rem 1rem;">
|
|
<%= t(
|
|
"admin.analytics.show.snapshot_meta",
|
|
device: t("admin.analytics.devices.#{@snapshot.device}"),
|
|
at: l(@snapshot.captured_at, format: :short)
|
|
) %>
|
|
</p>
|
|
<% else %>
|
|
<div class="admin-heatmap-fallback" aria-hidden="true"></div>
|
|
<p class="muted admin-heatmap-fallback-note">
|
|
<%= t(
|
|
"admin.analytics.show.snapshot_missing",
|
|
device: t("admin.analytics.devices.#{@filters[:device]}")
|
|
) %>
|
|
</p>
|
|
<canvas
|
|
id="admin-heatmap"
|
|
class="admin-heatmap-overlay"
|
|
data-grid="<%= @grid %>"
|
|
data-max="<%= [@max_weight, 1].max %>"
|
|
data-cells="<%= @cells.map { |(x, y), c| { x: x, y: y, c: c } }.to_json %>"
|
|
></canvas>
|
|
<% end %>
|
|
</div>
|
|
<script src="/admin-analytics-heatmap.js?v=4" defer></script>
|
|
<% else %>
|
|
<p class="empty"><%= t("admin.analytics.show.no_points") %></p>
|
|
<% end %>
|
|
</section>
|