ahoy_captain 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) 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/helpers/countries.js +2261 -0
  10. data/app/components/ahoy_captain/combobox_component.html.erb +2 -2
  11. data/app/components/ahoy_captain/combobox_component.rb +1 -1
  12. data/app/components/ahoy_captain/dropdown_link_component.html.erb +2 -4
  13. data/app/components/ahoy_captain/dropdown_link_component.rb +4 -0
  14. data/app/components/ahoy_captain/previous_next_component.html.erb +8 -0
  15. data/app/components/ahoy_captain/previous_next_component.rb +11 -0
  16. data/app/components/ahoy_captain/stats/comparable_container_component.html.erb +1 -1
  17. data/app/components/ahoy_captain/stats/container_component.html.erb +1 -1
  18. data/app/components/ahoy_captain/sticky_nav_component.html.erb +26 -22
  19. data/app/components/ahoy_captain/sticky_nav_component.rb +8 -0
  20. data/app/components/ahoy_captain/tile_component.rb +7 -0
  21. data/app/controllers/ahoy_captain/locations/cities_controller.rb +22 -0
  22. data/app/controllers/ahoy_captain/locations/countries_controller.rb +22 -0
  23. data/app/controllers/ahoy_captain/locations/maps_controller.rb +24 -0
  24. data/app/controllers/ahoy_captain/locations/regions_controller.rb +22 -0
  25. data/app/helpers/ahoy_captain/application_helper.rb +0 -2
  26. data/app/models/ahoy_captain/range_from_params.rb +4 -0
  27. data/app/views/ahoy_captain/locations/maps/show.html.erb +3 -0
  28. data/app/views/ahoy_captain/properties/_form.html.erb +1 -1
  29. data/app/views/ahoy_captain/roots/show.html.erb +46 -54
  30. data/app/views/ahoy_captain/stats/base/index.html.erb +1 -0
  31. data/app/views/ahoy_captain/stats/show.html.erb +11 -6
  32. data/config/routes.rb +7 -3
  33. data/lib/ahoy_captain/filters_configuration.rb +5 -5
  34. data/lib/ahoy_captain/version.rb +1 -1
  35. data/lib/ahoy_captain.rb +1 -0
  36. metadata +102 -12
  37. data/app/assets/javascript/ahoy_captain/controllers/active_links_controller.js +0 -30
  38. data/app/controllers/ahoy_captain/cities_controller.rb +0 -20
  39. data/app/controllers/ahoy_captain/countries_controller.rb +0 -20
  40. data/app/controllers/ahoy_captain/regions_controller.rb +0 -20
  41. /data/app/views/ahoy_captain/{cities → locations/cities}/index.html+details.erb +0 -0
  42. /data/app/views/ahoy_captain/{cities → locations/cities}/index.html.erb +0 -0
  43. /data/app/views/ahoy_captain/{countries → locations/countries}/index.html+details.erb +0 -0
  44. /data/app/views/ahoy_captain/{countries → locations/countries}/index.html.erb +0 -0
  45. /data/app/views/ahoy_captain/{regions → locations/regions}/index.html+details.erb +0 -0
  46. /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.1"
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