ahoy_captain 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +23 -2
  3. data/app/assets/javascript/ahoy_captain/controllers/application_controller.js +12 -0
  4. data/app/assets/javascript/ahoy_captain/controllers/combobox_controller.js +50 -20
  5. data/app/assets/javascript/ahoy_captain/controllers/frame_link_controller.js +20 -0
  6. data/app/assets/javascript/ahoy_captain/controllers/line_chart_controller.js +67 -4
  7. data/app/assets/javascript/ahoy_captain/controllers/map_controller.js +47 -0
  8. data/app/assets/javascript/ahoy_captain/controllers/predicate_select_controller.js +1 -0
  9. data/app/assets/javascript/ahoy_captain/controllers/property_filter_controller.js +0 -1
  10. data/app/assets/javascript/ahoy_captain/helpers/countries.js +2261 -0
  11. data/app/components/ahoy_captain/combobox_component.html.erb +2 -2
  12. data/app/components/ahoy_captain/combobox_component.rb +1 -1
  13. data/app/components/ahoy_captain/dropdown_link_component.html.erb +2 -4
  14. data/app/components/ahoy_captain/dropdown_link_component.rb +4 -0
  15. data/app/components/ahoy_captain/previous_next_component.html.erb +8 -0
  16. data/app/components/ahoy_captain/previous_next_component.rb +11 -0
  17. data/app/components/ahoy_captain/stats/comparable_container_component.html.erb +1 -1
  18. data/app/components/ahoy_captain/stats/container_component.html.erb +1 -1
  19. data/app/components/ahoy_captain/sticky_nav_component.html.erb +26 -22
  20. data/app/components/ahoy_captain/sticky_nav_component.rb +8 -0
  21. data/app/components/ahoy_captain/tile_component.rb +7 -0
  22. data/app/controllers/ahoy_captain/locations/cities_controller.rb +22 -0
  23. data/app/controllers/ahoy_captain/locations/countries_controller.rb +22 -0
  24. data/app/controllers/ahoy_captain/locations/maps_controller.rb +24 -0
  25. data/app/controllers/ahoy_captain/locations/regions_controller.rb +22 -0
  26. data/app/helpers/ahoy_captain/application_helper.rb +0 -2
  27. data/app/models/ahoy_captain/range_from_params.rb +4 -0
  28. data/app/views/ahoy_captain/locations/maps/show.html.erb +3 -0
  29. data/app/views/ahoy_captain/properties/_form.html.erb +1 -1
  30. data/app/views/ahoy_captain/roots/show.html.erb +46 -54
  31. data/app/views/ahoy_captain/stats/base/index.html.erb +1 -0
  32. data/app/views/ahoy_captain/stats/show.html.erb +11 -6
  33. data/config/routes.rb +7 -3
  34. data/lib/ahoy_captain/filters_configuration.rb +5 -5
  35. data/lib/ahoy_captain/version.rb +1 -1
  36. data/lib/ahoy_captain.rb +1 -0
  37. metadata +102 -12
  38. data/app/assets/javascript/ahoy_captain/controllers/active_links_controller.js +0 -30
  39. data/app/controllers/ahoy_captain/cities_controller.rb +0 -20
  40. data/app/controllers/ahoy_captain/countries_controller.rb +0 -20
  41. data/app/controllers/ahoy_captain/regions_controller.rb +0 -20
  42. /data/app/views/ahoy_captain/{cities → locations/cities}/index.html+details.erb +0 -0
  43. /data/app/views/ahoy_captain/{cities → locations/cities}/index.html.erb +0 -0
  44. /data/app/views/ahoy_captain/{countries → locations/countries}/index.html+details.erb +0 -0
  45. /data/app/views/ahoy_captain/{countries → locations/countries}/index.html.erb +0 -0
  46. /data/app/views/ahoy_captain/{regions → locations/regions}/index.html+details.erb +0 -0
  47. /data/app/views/ahoy_captain/{regions → locations/regions}/index.html.erb +0 -0
@@ -10,7 +10,7 @@
10
10
  >
11
11
  <div data-action="click->combobox#toggleOpen" data-combobox-target="box"
12
12
  class="
13
- bg-gray-900 ring-0 focus-within:ring-0 focus-within:ring-0 focus:ring-0 focus:outline-none w-full rounded-md px-4 py-2 text-sm
13
+ bg-gray-900 ring-0 focus-within:ring-0 focus-within:ring-0 focus:ring-0 focus:outline-none w-full rounded-md py-2 text-sm
14
14
  w-full"
15
15
  data-combobox-box-open-class="border-secondary-500 ring-1 ring-secondary-500">
16
16
  <select data-combobox-target="select" style="display:none;"
@@ -19,7 +19,7 @@
19
19
  id="<%= @select_html[:id] || "filter_#{@name}" %>"
20
20
  <% @select_html.each do |k,v| %><%=k %>="<%=v %>"<% end %>
21
21
  ><% @value.each do |value| %><option value="<%= value %>" selected><%= value %></option><% end %></select>
22
- <div data-combobox-target="selected" class="" style="display:none;"></div>
22
+ <div data-combobox-target="selected" class="px-2" style="display:none;"></div>
23
23
  <input data-combobox-target="input"
24
24
  data-action="input->combobox#onInput"
25
25
  class="input input-sm input-ghost w-full inline-block rounded-md focus:outline-none focus:ring-0 focus:bg-gray-900"
@@ -5,7 +5,7 @@ module AhoyCaptain
5
5
  @multiple = multiple
6
6
  @column = column
7
7
  @url = url
8
- @value = value
8
+ @value = Array(value)
9
9
  @select_html = select_html
10
10
  @disabled = disabled
11
11
  end
@@ -1,13 +1,11 @@
1
- <div class="dropdown dropdown-end " data-controller='dropdown-label'>
1
+ <div class="dropdown dropdown-end" data-controller='dropdown-label'>
2
2
  <label
3
3
  tabindex="0"
4
4
  class="cursor-pointer flex <%= classes %>"
5
5
  data-action='click->dropdown-label#removeHidden'
6
6
  >
7
7
  <span data-dropdown-label-target="label"><%= title %></span>
8
- <svg class="h-8 w-8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
9
- <path fill="currentColor" d="M16.53 8.97a.75.75 0 0 1 0 1.06l-4 4a.75.75 0 0 1-1.06 0l-4-4a.75.75 0 1 1 1.06-1.06L12 12.44l3.47-3.47a.75.75 0 0 1 1.06 0Z" clip-rule="evenodd" />
10
- </svg>
8
+
11
9
  </label>
12
10
  <ul class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52" data-dropdown-label-target="close">
13
11
  <% options.each do |option| %>
@@ -9,6 +9,10 @@ class AhoyCaptain::DropdownLinkComponent < ViewComponent::Base
9
9
  @classes = classes
10
10
  end
11
11
 
12
+ def link_to(name, url, **options)
13
+ self.with_option_content view_context.link_to name, url, options
14
+ end
15
+
12
16
  private
13
17
 
14
18
  attr_reader :title, :classes
@@ -0,0 +1,8 @@
1
+ <div class="rounded shadow bg-white cursor-pointer dark:bg-gray-800 flex h-8">
2
+ <button class="flex items-center px-1 sm:px-2 border-r border-gray-300 rounded-l dark:border-gray-500 dark:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-900" type="button">
3
+ <svg class="feather h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
4
+ </button>
5
+ <button class="flex items-center px-1 sm:px-2 rounded-r dark:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-900" type="button">
6
+ <svg class="feather h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
7
+ </button>
8
+ </div>
@@ -0,0 +1,11 @@
1
+ module AhoyCaptain
2
+ class PreviousNextComponent < ViewComponent::Base
3
+ def initialize(range)
4
+ @range = range
5
+ end
6
+
7
+ def render?
8
+ false
9
+ end
10
+ end
11
+ end
@@ -1,4 +1,4 @@
1
- <a href="<%= @url %>" class="relative px-4 md:px-6 w-1/2 my-4 w-auto group cursor-pointer" data-turbo-frame="chart">
1
+ <a href="<%= @url %>" class="relative px-4 md:px-6 w-1/2 my-4 w-auto group cursor-pointer" data-controller="frame-link" data-turbo-frame="chart">
2
2
  <div>
3
3
  <h5 class="text-sm font-bold uppercase whitespace-nowrap flex w-content border-transparent tooltip tooltip-bottom "
4
4
  data-active-links-target="link" data-tip="<%= tooltip %>">
@@ -1,4 +1,4 @@
1
- <a href="<%= @url %>" class="relative px-4 md:px-6 w-1/2 my-4 lg:w-auto group cursor-pointer" data-turbo-frame="chart">
1
+ <a href="<%= @url %>" class="relative px-4 md:px-6 w-1/2 my-4 lg:w-auto group cursor-pointer" data-controller="frame-link" data-turbo-frame="chart">
2
2
  <div>
3
3
  <h5 class="text-sm font-bold uppercase whitespace-nowrap flex w-content border-transparent tooltip tooltip-bottom "
4
4
  data-active-links-target="link">
@@ -1,28 +1,32 @@
1
- <div class="max-w-6xl flex justify-between sticky top-0 min-h-sm z-[99999] py-4 bg-base-100">
2
- <div class="flex items-center">
3
- <a href="/">
4
- <img src="<%= image_path "ahoy_captain/logo.png" %>" alt="AhoyCaptainLogo" class='max-h-20 md:h-16 rounded-full'>
5
- </a>
6
- <% if tag_list_hidden? %>
7
- <%= render AhoyCaptain::Filter::TagContainerComponent.new %>
8
- <% else %>
9
- <%= realtime_update %>
10
- <% end %>
11
- </div>
12
- <div class="flex flex-row-reverse col-span-2 items-center gap-3">
13
- <%= render AhoyCaptain::ComparisonLinkComponent.new %>
1
+ <div class=" sticky top-0 min-h-sm z-[99999] py-4 bg-base-100 pb-4">
2
+ <div class="max-w-6xl mx-auto max-w-6xl flex justify-between ">
3
+ <div class="flex items-center">
4
+ <a href="/">
5
+ <img src="<%= image_path "ahoy_captain/logo.png" %>" alt="AhoyCaptainLogo" class='max-h-20 md:h-16 rounded-full'>
6
+ </a>
7
+ <% if tag_list_hidden? %>
8
+ <%= render AhoyCaptain::Filter::TagContainerComponent.new %>
9
+ <% else %>
10
+ <%= realtime_update %>
11
+ <% end %>
12
+ </div>
13
+ <div class="flex flex-row-reverse col-span-2 items-center gap-3">
14
+ <%= render AhoyCaptain::ComparisonLinkComponent.new %>
15
+ <%= render AhoyCaptain::PreviousNextComponent.new(range) %>
14
16
 
15
- <%= render AhoyCaptain::DropdownLinkComponent.new(title: params[:start_date] ? "Custom Range" : (AhoyCaptain.config.ranges.find(params[:period] || AhoyCaptain.config.ranges.default).try(:label) || "Period"), classes: 'btn btn-sm btn-base-100 no-underline hover:bg-base-100') do |dropdown| %>
16
- <% dropdown.with_option do %>
17
- <% AhoyCaptain.config.ranges.each do |param, range| %>
18
- <a class='link no-underline' href="<%= request.path %>?<%= request.query_parameters.except("start_date", "end_date", "date", "compare_to_start_date", "compare_to_end_date").merge("period" => param).to_query %>"><%= range.label %></a>
19
- <% end %>
17
+ <%= render AhoyCaptain::DropdownLinkComponent.new(title: params[:start_date] ? custom_range_label : (AhoyCaptain.config.ranges.find(params[:period] || AhoyCaptain.config.ranges.default).try(:label) || "Period"), classes: 'btn btn-sm btn-base-100 no-underline hover:bg-base-100') do |dropdown| %>
18
+ <% dropdown.with_option do %>
19
+ <% AhoyCaptain.config.ranges.each do |param, range| %>
20
+ <a class='link no-underline' href="<%= request.path %>?<%= request.query_parameters.except("start_date", "end_date", "date", "compare_to_start_date", "compare_to_end_date").merge("period" => param).to_query %>"><%= range.label %></a>
21
+ <% end %>
20
22
 
21
- <a class='link no-underline ' href='#' onclick="event.preventDefault(); customRangeModal.showModal()">Custom range</a>
22
- <a class='link no-underline ' href='<%= AhoyCaptain::Engine.routes.url_helpers.root_path(**helpers.search_params.merge(comparison: !compare_mode?)) %>'><%= compare_mode? ? "Disable Comparison" : "Compare" %></a>
23
+ <a class='link no-underline ' href='#' onclick="event.preventDefault(); customRangeModal.showModal()">Custom Range</a>
24
+ <a class='link no-underline ' href='<%= AhoyCaptain::Engine.routes.url_helpers.root_path(**helpers.search_params.merge(comparison: !compare_mode?)) %>'><%= compare_mode? ? "Disable Comparison" : "Compare" %></a>
25
+ <% end %>
23
26
  <% end %>
24
- <% end %>
25
27
 
26
- <%= render AhoyCaptain::Filter::DropdownComponent.new(filters: filters) %>
28
+ <%= render AhoyCaptain::Filter::DropdownComponent.new(filters: filters) %>
29
+ </div>
27
30
  </div>
31
+
28
32
  </div>
@@ -10,6 +10,14 @@ class AhoyCaptain::StickyNavComponent < ViewComponent::Base
10
10
  @filters ||= ::AhoyCaptain::FilterParser.parse(request)
11
11
  end
12
12
 
13
+ def custom_range_label
14
+ if range.custom?
15
+ [range.starts_at, range.ends_at].map { |date| date.strftime('%b %d, %Y') }.join("-")
16
+ else
17
+ "Custom Range"
18
+ end
19
+ end
20
+
13
21
  def tag_list_hidden?
14
22
  filters.values.map(&:values).flatten.size < ::AhoyCaptain::FilterParser::FILTER_MENU_MAX_SIZE
15
23
  end
@@ -11,6 +11,13 @@ class AhoyCaptain::TileComponent < ViewComponent::Base
11
11
  @wide = wide
12
12
  end
13
13
 
14
+ def link_to(name, url, **options)
15
+ options[:class] = "inline-block h-5 font-semibold"
16
+ options[:data] ||= {}
17
+ options[:data].merge!(controller: "frame-link")
18
+ view_context.link_to name, url, **options
19
+ end
20
+
14
21
  private
15
22
 
16
23
  attr_reader :title, :wide
@@ -0,0 +1,22 @@
1
+ module AhoyCaptain
2
+ module Locations
3
+ class CitiesController < ApplicationController
4
+ include AhoyCaptain::Limitable
5
+
6
+ before_action do
7
+ if Widget.disabled?(:locations, :cities)
8
+ raise Widget::WidgetDisabled.new("Widget disabled", :geography)
9
+ end
10
+ end
11
+
12
+ def index
13
+ results = cached(:cities) do
14
+ CityQuery.call(params)
15
+ .limit(limit)
16
+ end
17
+
18
+ @cities = paginate(results).map { |city| CityDecorator.new(city, self) }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module AhoyCaptain
2
+ module Locations
3
+ class CountriesController < ApplicationController
4
+ include Limitable
5
+
6
+ before_action do
7
+ if Widget.disabled?(:locations, :countries)
8
+ raise Widget::WidgetDisabled.new("Widget disabled", :geography)
9
+ end
10
+ end
11
+
12
+ def index
13
+ results = cached(:countries) do
14
+ CountryQuery.call(params)
15
+ .limit(limit)
16
+ end
17
+
18
+ @countries = paginate(results).map { |country| CountryDecorator.new(country, self) }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ module AhoyCaptain
2
+ module Locations
3
+ class MapsController < ApplicationController
4
+ include Limitable
5
+
6
+ before_action do
7
+ if Widget.disabled?(:locations, :map)
8
+ raise Widget::WidgetDisabled.new("Widget disabled", :geography)
9
+ end
10
+ end
11
+
12
+ def show
13
+ if request.variant.include?(:details)
14
+ results = CountryQuery.call(params)
15
+ results = results.limit(limit)
16
+ @countries = paginate(results).map { |country| CountryDecorator.new(country, self) }
17
+ render template: 'ahoy_captain/locations/countries/index'
18
+ else
19
+ @countries = visit_query.group("country").count
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ module AhoyCaptain
2
+ module Locations
3
+ class RegionsController < ApplicationController
4
+ include Limitable
5
+
6
+ before_action do
7
+ if Widget.disabled?(:locations, :regions)
8
+ raise Widget::WidgetDisabled.new("Widget disabled", :geography)
9
+ end
10
+ end
11
+
12
+ def index
13
+ results = cached(:regions) do
14
+ RegionQuery.call(params)
15
+ .limit(limit)
16
+ end
17
+
18
+ @regions = paginate(results).map { |region| RegionDecorator.new(region, self) }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -56,8 +56,6 @@ module AhoyCaptain
56
56
  :period,
57
57
  :interval,
58
58
  :comparison,
59
- :compare_to_start_date,
60
- :compare_to_end_date
61
59
  ]
62
60
 
63
61
  # properties stuff falls into current_property_filter
@@ -53,6 +53,10 @@ module AhoyCaptain
53
53
  @range.end.nil?
54
54
  end
55
55
 
56
+ def custom?
57
+ @raw[:start_date].present? && @raw[:end_date].present?
58
+ end
59
+
56
60
  def [](value)
57
61
  if value == 0
58
62
  starts_at
@@ -0,0 +1,3 @@
1
+ <%= turbo_frame_tag :geography do %>
2
+ <canvas style="position: relative; width: 500px; height: 300px;" data-controller="map" data-map-data-value="<%= @countries.to_json %>"></canvas>
3
+ <% end %>
@@ -1,4 +1,4 @@
1
- <select class="select text-accent-content bg-accent" data-controller="properties" data-action="change->properties#handleChange">
1
+ <select class="select text-accent select-sm w-full max-w-sm" data-controller="properties" data-action="change->properties#handleChange">
2
2
  <option></option>
3
3
  <% @options.each do |key, value| %>
4
4
  <option value="<%= property_path(id: key) %>" <%= 'selected' if local_assigns[:selected] == value %>><%= value %></option>
@@ -1,15 +1,14 @@
1
- <main class='min-h-screen pb-4 max-w-6xl mx-auto' data-controller="application" data-action="combobox:init@window->application#comboboxInit">
1
+ <main class='w-screen overflow-hidden' data-action="combobox:init@window->application#comboboxInit">
2
2
  <%= render AhoyCaptain::StickyNavComponent.new do |nav| %>
3
3
  <% nav.with_realtime_update do %>
4
4
  <%= turbo_frame_tag :realtime, src: realtime_path, data: { controller: "realtime", "realtime-interval-value" => AhoyCaptain.config.realtime_interval.to_i }, loading: :lazy %>
5
5
  <% end %>
6
6
  <% end %>
7
7
 
8
- <div class="grid grid-cols-1 lg:grid-cols-2 grid-flow-row gap-4">
8
+ <div class="grid grid-cols-1 lg:grid-cols-2 grid-flow-row gap-4 min-h-screen pb-4 max-w-6xl mx-auto">
9
9
  <%= render AhoyCaptain::TileComponent.new(wide: true, classes: "p-4 m-2") do |component| %>
10
10
  <% component.with_statistic_display do %>
11
11
  <%= turbo_frame_tag :stats, src: stats_path(search_params), loading: :lazy %>
12
-
13
12
  <%= turbo_frame_tag :chart, src: stats_unique_visitors_path(search_params) do %>
14
13
  <% end %>
15
14
  <% end %>
@@ -17,21 +16,19 @@
17
16
  <% end %>
18
17
 
19
18
  <%= render AhoyCaptain::TileComponent.new(title: 'Top Sources') do |component| %>
20
- <% component.with_display_links do %>
21
- <div data-controller="active-links">
22
- <a href="<%= sources_path(search_params) %>" data-turbo-frame="sources" class="" data-active-links-target="link">All</a>
23
- <%= render AhoyCaptain::DropdownLinkComponent.new(title: "Campaign") do |dropdown| %>
24
- <% %w{utm_source utm_medium utm_term utm_content utm_campaign}.each do |source| %>
25
- <% dropdown.with_option do %>
26
- <a href="<%= public_send("campaign_#{source}_path".to_sym, **search_params) %>" class=""
27
- data-turbo-frame="sources"
28
- data-active-links-target="link">
29
- <%= source.titleize.gsub("Utm", "UTM") %>
30
- </a>
19
+ <% component.with_display_links do %>
20
+ <div class="flex text-xs font-medium text-gray-400 space-x-2">
21
+ <div class="relative inline-block text-left">
22
+ <%= component.link_to "All", sources_path(search_params), data: { turbo_frame: "sources" } %>
23
+ <%= render AhoyCaptain::DropdownLinkComponent.new(title: "Campaign") do |dropdown| %>
24
+ <% %w{utm_source utm_medium utm_term utm_content utm_campaign}.each do |source| %>
25
+ <%= dropdown.link_to source.titleize.gsub("Utm", "UTM"), public_send("campaign_#{source}_path".to_sym, **search_params), data: { turbo_frame: "sources" } %>
26
+ <% end %>
31
27
  <% end %>
32
- <% end %>
33
- <% end %>
34
- </div>
28
+ </div>
29
+ </div>
30
+
31
+
35
32
  <% end %>
36
33
  <% component.with_statistic_display do %>
37
34
  <%= turbo_frame_tag :sources, src: sources_path(search_params), loading: :lazy %>
@@ -44,14 +41,10 @@
44
41
  <%= render AhoyCaptain::TileComponent.new(title: 'Top Pages') do |component| %>
45
42
  <% component.with_display_links do %>
46
43
  <div class="flex text-xs font-medium text-gray-400 space-x-2">
47
- <div class="relative inline-block text-left" data-controller="active-links"><div>
48
- <a href="<%= top_pages_path(search_params) %>"
49
- data-turbo-frame="pages"
50
- class="inline-block h-5 font-semibold"
51
- data-active-links-target="link"
52
- data-action="click->tile#setTitle">Top Pages</a>
53
- <a href="<%= entry_pages_path(search_params) %>" data-turbo-frame="pages" class="inline-block h-5 font-semibold" data-active-links-target="link" data-action="click->tile#setTitle">Entry Pages</a>
54
- <a href="<%= exit_pages_path(search_params) %>" data-turbo-frame="pages" class="inline-block h-5 font-semibold" data-active-links-target="link" data-action="click->tile#setTitle">Exit Pages</a>
44
+ <div class="relative inline-block text-left"><div>
45
+ <%= component.link_to "Top Pages", top_pages_path(search_params), data: { action: "click->tile#setTitle", turbo_frame: "pages" } %>
46
+ <%= component.link_to "Entry Pages", entry_pages_path(search_params), data: { action: "click->tile#setTitle", turbo_frame: "pages" } %>
47
+ <%= component.link_to "Exit Pages", exit_pages_path(search_params), data: { action: "click->tile#setTitle", turbo_frame: "pages" } %>
55
48
  </div>
56
49
  </div>
57
50
  </div>
@@ -65,33 +58,32 @@
65
58
  <% end %>
66
59
  <% end %>
67
60
 
68
- <%= render AhoyCaptain::TileComponent.new(title: 'Countries') do |component| %>
61
+ <%= render AhoyCaptain::TileComponent.new(title: 'Map') do |component| %>
69
62
  <% component.with_display_links do %>
70
63
  <div class="flex text-xs font-medium text-gray-400 space-x-2">
71
- <div class="relative inline-block text-left" data-controller="active-links">
72
-
73
- <a href="<%= countries_path(search_params) %>" data-turbo-frame="geography" class="inline-block h-5 font-semibold" data-active-links-target="link" data-action="click->tile#setTitle">Countries</a>
74
- <a href="<%= regions_path(search_params) %>" data-turbo-frame="geography" class="inline-block h-5 font-semibold" data-active-links-target="link" data-action="click->tile#setTitle">Regions</a>
75
- <a href="<%= cities_path(search_params) %>" data-turbo-frame="geography" class="inline-block h-5 font-semibold" data-active-links-target="link" data-action="click->tile#setTitle">Cities</a>
64
+ <div class="relative inline-block text-left">
65
+ <%= component.link_to "Map", locations_map_path(search_params), data: { action: "click->tile#setTitle", turbo_frame: "geography" } %>
66
+ <%= component.link_to "Countries", locations_countries_path(search_params), data: { action: "click->tile#setTitle", turbo_frame: "geography" } %>
67
+ <%= component.link_to "Regions", locations_regions_path(search_params), data: { action: "click->tile#setTitle", turbo_frame: "geography" } %>
68
+ <%= component.link_to "Cities", locations_cities_path(search_params), data: { action: "click->tile#setTitle", turbo_frame: "geography" } %>
76
69
  </div>
77
70
  </div>
78
71
  <% end %>
79
72
  <% component.with_statistic_display do %>
80
- <%= turbo_frame_tag :geography, src: countries_path(search_params), loading: :lazy %>
73
+ <%= turbo_frame_tag :geography, src: locations_map_path(search_params), loading: :lazy %>
81
74
  <% end %>
82
75
  <% component.with_details_cta do %>
83
- <button data-action="click->details-modal#openModal" data-controller="details-modal" data-details-modal-target-value="#geography" class="link no-underline ">Details</button>
76
+ <button data-action="click->details-modal#openModal" data-controller="details-modal" data-details-modal-target-value="#geography" class="link no-underline ">Details</button>
84
77
  <% end %>
85
78
  <% end %>
86
79
 
87
80
  <%= render AhoyCaptain::TileComponent.new(title: 'Devices') do |component| %>
88
81
  <% component.with_display_links do %>
89
82
  <div class="flex text-xs font-medium text-gray-400 space-x-2">
90
- <div class="relative inline-block text-left" data-controller="active-links">
91
-
92
- <a href="<%= devices_browsers_path(search_params) %>" data-turbo-frame="devices" class=" inline-block h-5 font-semibold" data-active-links-target="link">Browser</a>
93
- <a href="<%= devices_operating_systems_path(search_params) %>" data-turbo-frame="devices" class="inline-block h-5 font-semibold" data-active-links-target="link">OS</a>
94
- <a href="<%= devices_device_types_path(search_params) %>" data-turbo-frame="devices" class="inline-block h-5 font-semibold" data-active-links-target="link">Size</a>
83
+ <div class="relative inline-block text-left">
84
+ <%= component.link_to "Browser", devices_browsers_path(search_params), data: { turbo_frame: "devices" } %>
85
+ <%= component.link_to "OS", devices_operating_systems_path(search_params), data: { turbo_frame: "devices" } %>
86
+ <%= component.link_to "Size", devices_device_types_path(search_params), data: { turbo_frame: "devices" } %>
95
87
  </div>
96
88
  </div>
97
89
  <% end %>
@@ -105,23 +97,23 @@
105
97
  <%= render AhoyCaptain::TileComponent.new(title: "Goals and Funnels", wide: true, classes: "p-4 m-2") do |component| %>
106
98
  <% component.with_display_links do %>
107
99
  <div>
108
- <div data-controller="active-links">
109
-
110
- <a href="<%= goals_path(search_params) %>" data-turbo-frame="goals" class="link ">
111
- Goals
112
- </a>
113
- <a href="<%= properties_path(search_params) %>" data-turbo-frame="goals" class="link ">
114
- Properties
115
- </a>
116
- <%= render AhoyCaptain::DropdownLinkComponent.new(title: "Funnels") do |dropdown| %>
117
- <% AhoyCaptain.config.funnels.each do |id, funnel| %>
118
- <% dropdown.with_option do %>
119
- <a href="<%= funnel_path(id, search_params) %>" data-turbo-frame="goals" class="link ">
120
- <%= funnel.title %>
121
- </a>
100
+ <div >
101
+ <div class="flex text-xs font-medium text-gray-400 space-x-2">
102
+ <div class="relative inline-block text-left">
103
+ <a href="<%= goals_path(search_params) %>" data-turbo-frame="goals" class="inline-block h-5 font-semibold" data-controller="frame-link" data-action="click->tile#setTitle">Goals</a>
104
+ <a href="<%= properties_path(search_params) %>" data-turbo-frame="goals" class="inline-block h-5 font-semibold" data-controller="frame-link" data-action="click->tile#setTitle">Properties</a>
105
+ <%= render AhoyCaptain::DropdownLinkComponent.new(title: "Funnels") do |dropdown| %>
106
+ <% AhoyCaptain.config.funnels.each do |id, funnel| %>
107
+ <% dropdown.with_option do %>
108
+ <a href="<%= funnel_path(id, search_params) %>" data-turbo-frame="goals" class="link " data-action="click->tile#setTitle" title="<%= funnel.title %> Funnel">
109
+ <%= funnel.title %>
110
+ </a>
111
+ <% end %>
112
+ <% end %>
122
113
  <% end %>
123
- <% end %>
124
- <% end %>
114
+ </div>
115
+ </div>
116
+
125
117
  </div>
126
118
  </div>
127
119
  <% end %>
@@ -7,6 +7,7 @@
7
7
  <% end %>
8
8
  </div>
9
9
  <div>
10
+ <canvas id="overlay" width="600" height="400" style="position:absolute;pointer-events:none;"></canvas>
10
11
 
11
12
  <% if compare_mode? %>
12
13
  <canvas class="h-[300px] w-full"
@@ -1,10 +1,15 @@
1
1
  <%= turbo_frame_tag :stats, data: { controller: "active-frame-link" } do %>
2
2
  <dl class="grid grid-cols-1 divide-y divide-base-200 overflow-hidden rounded-lg grid-cols-2 md:grid-cols-6 md:divide-y-0" data-controller="active-links" data-active-links-classes-value='["text-primary"]'>
3
- <%= render stats_container(@presenter.unique_visitors, stats_unique_visitors_url(search_params), "Unique Visits", :number_with_delimiter, true) %>
4
- <%= render stats_container(@presenter.total_visits, stats_total_visits_path(search_params), "Total Visits", :number_with_delimiter) %>
5
- <%= render stats_container(@presenter.total_pageviews, stats_total_pageviews_path(search_params), "Total Pageviews", :number_with_delimiter) %>
6
- <%= render stats_container(@presenter.views_per_visit, stats_views_per_visits_path(search_params), "Views per Visit", :number_with_delimiter) %>
7
- <%= render stats_container(@presenter.bounce_rate, stats_bounce_rates_path(search_params), "Bounce Rate", :number_with_delimiter) %>
8
- <%= render stats_container(@presenter.visit_duration, stats_visit_durations_url(search_params), "Visit Duration", :number_to_duration) %>
3
+ <% if @presenter.send(:range).realtime? %>
4
+ <%= render stats_container(@presenter.unique_visitors, stats_unique_visitors_url(search_params), "Unique Visits (30 min)", :number_with_delimiter, true) %>
5
+ <%= render stats_container(@presenter.total_pageviews, stats_total_pageviews_path(search_params), "Total Pageviews (30 min)", :number_with_delimiter) %>
6
+ <% else %>
7
+ <%= render stats_container(@presenter.unique_visitors, stats_unique_visitors_url(search_params), "Unique Visits", :number_with_delimiter, true) %>
8
+ <%= render stats_container(@presenter.total_visits, stats_total_visits_path(search_params), "Total Visits", :number_with_delimiter) %>
9
+ <%= render stats_container(@presenter.total_pageviews, stats_total_pageviews_path(search_params), "Total Pageviews", :number_with_delimiter) %>
10
+ <%= render stats_container(@presenter.views_per_visit, stats_views_per_visits_path(search_params), "Views per Visit", :number_with_delimiter) %>
11
+ <%= render stats_container(@presenter.bounce_rate, stats_bounce_rates_path(search_params), "Bounce Rate", :number_with_delimiter) %>
12
+ <%= render stats_container(@presenter.visit_duration, stats_visit_durations_url(search_params), "Visit Duration", :number_to_duration) %>
13
+ <% end %>
9
14
  </dl>
10
15
  <% end %>
data/config/routes.rb CHANGED
@@ -12,15 +12,19 @@ AhoyCaptain::Engine.routes.draw do
12
12
  get "/devices/#{k}" => 'devices#index', defaults: { devices_type: v }, as: "devices_#{k}"
13
13
  end
14
14
 
15
+ namespace :locations do
16
+ resource :map, only: [:show]
17
+ resources :countries, only: [:index]
18
+ resources :regions, only: [:index]
19
+ resources :cities, only: [:index]
20
+ end
21
+
15
22
  resources :properties, only: [:index, :show]
16
23
  resource :export, only: [:show]
17
24
  resource :realtime, only: [:show]
18
25
  resources :funnels, only: [:show]
19
26
  resources :goals, only: [:index]
20
27
  resource :stats, only: [:show]
21
- resources :countries, only: [:index]
22
- resources :regions, only: [:index]
23
- resources :cities, only: [:index]
24
28
  resources :campaigns, only: [:index]
25
29
  resources :sources, only: [:index]
26
30
  resources :exit_pages, only: [:index]
@@ -31,11 +31,11 @@ module AhoyCaptain
31
31
  end
32
32
 
33
33
  config.register("UTM Tags") do
34
- filter column: :utm_medium, label: "UTM Medium", url: :filters_utm_mediums_path, predicates: [:in, :not_in]
35
- filter column: :utm_source, label: "UTM Source", url: :filters_utm_sources_path, predicates: [:in, :not_in]
36
- filter column: :utm_campaign, label: "UTM Campaign", url: :filters_utm_campaigns_path, predicates: [:in, :not_in]
37
- filter column: :utm_term, label: "UTM Term", url: :filters_utm_terms_path, predicates: [:in, :not_in]
38
- filter column: :utm_content, label: "UTM Content", url: :filters_utm_contents_path, predicates: [:in, :not_in]
34
+ filter column: :utm_medium, label: "UTM Medium", url: :filters_utm_mediums_path, predicates: [:in, :not_in, :cont]
35
+ filter column: :utm_source, label: "UTM Source", url: :filters_utm_sources_path, predicates: [:in, :not_in, :cont]
36
+ filter column: :utm_campaign, label: "UTM Campaign", url: :filters_utm_campaigns_path, predicates: [:in, :not_in, :cont]
37
+ filter column: :utm_term, label: "UTM Term", url: :filters_utm_terms_path, predicates: [:in, :not_in, :cont]
38
+ filter column: :utm_content, label: "UTM Content", url: :filters_utm_contents_path, predicates: [:in, :not_in, :cont]
39
39
  end
40
40
 
41
41
  config.register("Goal") do
@@ -1,3 +1,3 @@
1
1
  module AhoyCaptain
2
- VERSION = "0.10.0"
2
+ VERSION = "0.11.0"
3
3
  end
data/lib/ahoy_captain.rb CHANGED
@@ -32,6 +32,7 @@ module AhoyCaptain
32
32
  pin "Chart.bundle", to: "Chart.bundle.js"
33
33
  pin "chartjs-plugin-datalabels", to: "https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2", preload: true
34
34
  pin "classnames", to: "https://cdnjs.cloudflare.com/ajax/libs/classnames/2.3.2/index.min.js", preload: true
35
+ pin "chartjs-chart-geo", to: "https://unpkg.com/chartjs-chart-geo@4", preload: true
35
36
  pin_all_from AhoyCaptain::Engine.root.join("app/assets/javascript/ahoy_captain/controllers"), under: "controllers", to: "ahoy_captain/controllers"
36
37
  pin_all_from AhoyCaptain::Engine.root.join("app/assets/javascript/ahoy_captain/helpers"), under: "helpers", to: "ahoy_captain/helpers"
37
38
  end