%= content_for :title, "Analytics" %>
<%= turbo_frame_tag 'main' do %>
<%= turbo_stream.append 'flash', partial: 'layouts/flash' %>
<%= yield(:title) %>
<% { "all" => "All time", "30d" => "30 days", "7d" => "7 days", "24h" => "24 hours" }.each do |key, label| %>
<%= link_to label,
url_for(period: key),
class: class_names("analytics-period", current: period == key),
data: { turbo_frame: "main", turbo_action: "advance" } %>
<% end %>
<% if DemoActivity::ENABLED %>
Demo data is on.
DemoActivity keeps inventing players with no gameplay to feed the leaderboard
banner, so the counts below are inflated and the funnel shows a large drop before "Facts".
Set DemoActivity::ENABLED = false and wipe the players table before go-live.
<% end %>
<%= render "card", label: "Players started", value: number_with_delimiter(@analytics.total_players) %>
<%= render "card", label: "Finished", value: number_with_delimiter(@analytics.completed_players),
sub: "#{@analytics.completion_rate}% of starters" %>
<%= render "card", label: "Median play time", value: format_duration(@analytics.median_duration) %>
<%= render "card", label: "Average score", value: (@analytics.average_score || "—") %>
Did you like this game?
<% ratings = @analytics.ratings %>
<% if ratings[:total].zero? %>
No votes yet.
<% else %>
<%= render "card", label: "Thumbs up", value: number_with_delimiter(ratings[:up]),
sub: "#{ratings[:up_share]}%", modifier: "positive" %>
<%= render "card", label: "Thumbs down", value: number_with_delimiter(ratings[:down]),
sub: "#{ratings[:down_share]}%", modifier: "negative" %>
<%= render "card", label: "Votes", value: number_with_delimiter(ratings[:total]),
sub: "#{ratings[:response_rate]}% of players who reached the results" %>
<% end %>
Funnel
How far players got. The last-save early exit is a branch off the stages, so it is
reported separately below rather than as a funnel step.
<% @analytics.funnel.each do |row| %>
<%= row[:label] %>
<%= number_with_delimiter(row[:reached]) %>
<%= row[:share] %>%
<% if row[:lost] > 0 %>
-<%= number_with_delimiter(row[:lost]) %> (<%= row[:lost_share] %>%)
<% end %>
<% end %>
Where unfinished players stopped
<% if @analytics.drop_off_points.empty? %>
Nobody has abandoned a game yet.
<% else %>
<% max = @analytics.drop_off_points.first[:count] %>
<% @analytics.drop_off_points.each do |row| %>
<%= row[:label] %>
<%= number_with_delimiter(row[:count]) %>
<% end %>
<% end %>
Answers by stage
<% @analytics.stages.each do |stage| %>
<%= stage[:index] %>
<%= stage[:node].title %>
<%= number_with_delimiter(stage[:total]) %> answers
<% stage[:answers].each do |answer| %>
<%= answer[:label] %>
<%= tag.em(answer[:node].template.humanize) %>
<%= number_with_delimiter(answer[:count]) %>
<%= answer[:share] %>%
<% answer[:outcomes].each do |outcome| %>
↳ <%= outcome[:label] %>
<%= tag.em(outcome[:node].template.humanize) %>
<%= number_with_delimiter(outcome[:count]) %>
<%= outcome[:share] %>% of chances
<% end %>
<% end %>
<% end %>
<% if @analytics.last_save[:node] %>
Last save early exit — <%= number_with_delimiter(@analytics.last_save[:total]) %> players
<% if @analytics.last_save[:total].zero? %>
No player has taken the early exit yet.
<% else %>
<% @analytics.last_save[:answers].each do |answer| %>
<%= answer[:label] %>
<%= number_with_delimiter(answer[:count]) %>
<%= answer[:share] %>%
<% end %>
<% end %>
<% end %>
Endings finished players, excluding early exits
<% @analytics.result_bands.each do |band| %>
<%= t("game.results.#{band[:band]}.headline", default: band[:band].to_s.humanize).html_safe %>
<%= number_with_delimiter(band[:count]) %>
<%= band[:share] %>%
<% end %>
<%= render "breakdown", title: "How they got here",
note: "Where the scan came from. Each printed QR code carries its own " \
"utm_content tag, so the rows below are the physical placements. " \
"The board game QR points at dice.ikeafoundation.org and is counted there, not here.",
rows: @analytics.by_source,
labeller: ->(key) { source_breakdown_label(key) } %>
<%= render "breakdown", title: "Devices", rows: @analytics.by_device,
labeller: ->(key) { device_breakdown_label(key) } %>
<%= render "breakdown", title: "Languages", rows: @analytics.by_locale,
labeller: ->(key) { language_breakdown_label(key) } %>
<%= render "breakdown", title: "Countries", rows: @analytics.by_country,
labeller: ->(key) { country_breakdown_label(key) } %>
<% end %>