ahoy_captain 0.81 → 0.83

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascript/ahoy_captain/controllers/active_links_controller.js +15 -0
  3. data/app/assets/javascript/ahoy_captain/controllers/filter_form_controller.js +13 -0
  4. data/app/assets/javascript/ahoy_captain/controllers/line_chart_controller.js +37 -0
  5. data/app/assets/javascript/ahoy_captain/controllers/predicate_select_controller.js +10 -0
  6. data/app/assets/javascript/ahoy_captain/controllers/realtime_controller.js +2 -0
  7. data/app/assets/javascript/ahoy_captain/controllers/search_select_controller.js +65 -0
  8. data/app/components/ahoy_captain/filter/modal_component.html.erb +6 -5
  9. data/app/components/ahoy_captain/filter/select_component.html.erb +13 -11
  10. data/app/components/ahoy_captain/filter/select_component.rb +21 -4
  11. data/app/components/ahoy_captain/table_component.html.erb +2 -35
  12. data/app/components/ahoy_captain/table_component.rb +13 -5
  13. data/app/components/ahoy_captain/tables/headers/devices_header_component.html.erb +3 -0
  14. data/app/components/ahoy_captain/tables/headers/devices_header_component.rb +9 -0
  15. data/app/components/ahoy_captain/tables/headers/goals_header_component.html.erb +6 -0
  16. data/app/components/ahoy_captain/tables/headers/goals_header_component.rb +9 -0
  17. data/app/components/ahoy_captain/tables/headers/header_component.html.erb +5 -0
  18. data/app/components/ahoy_captain/tables/headers/header_component.rb +12 -0
  19. data/app/components/ahoy_captain/tables/rows/devices_row_component.html.erb +5 -0
  20. data/app/components/ahoy_captain/tables/rows/devices_row_component.rb +12 -0
  21. data/app/components/ahoy_captain/tables/rows/goals_row_component.html.erb +11 -0
  22. data/app/components/ahoy_captain/tables/rows/goals_row_component.rb +12 -0
  23. data/app/components/ahoy_captain/tables/rows/row_component.html.erb +6 -0
  24. data/app/components/ahoy_captain/tables/rows/row_component.rb +41 -0
  25. data/app/controllers/ahoy_captain/filters/pages/entry_pages_controller.rb +2 -2
  26. data/app/controllers/ahoy_captain/filters/pages/exit_pages_controller.rb +1 -2
  27. data/app/controllers/ahoy_captain/filters/properties/names_controller.rb +11 -0
  28. data/app/controllers/ahoy_captain/filters/properties/values_controller.rb +15 -0
  29. data/app/controllers/ahoy_captain/stats/bounce_rates_controller.rb +1 -0
  30. data/app/controllers/ahoy_captain/stats/total_pageviews_controller.rb +1 -0
  31. data/app/controllers/ahoy_captain/stats/total_visits_controller.rb +1 -0
  32. data/app/controllers/ahoy_captain/stats/unique_visitors_controller.rb +1 -0
  33. data/app/controllers/ahoy_captain/stats/views_per_visits_controller.rb +3 -0
  34. data/app/controllers/ahoy_captain/stats/visit_durations_controller.rb +1 -0
  35. data/app/queries/ahoy_captain/application_query.rb +4 -3
  36. data/app/queries/ahoy_captain/event_query.rb +17 -15
  37. data/app/queries/ahoy_captain/visit_query.rb +1 -1
  38. data/app/views/ahoy_captain/devices/_table.html.erb +5 -0
  39. data/app/views/ahoy_captain/devices/index.html+details.erb +1 -1
  40. data/app/views/ahoy_captain/devices/index.html.erb +2 -2
  41. data/app/views/ahoy_captain/goals/index.html.erb +3 -35
  42. data/app/views/ahoy_captain/layouts/application.html.erb +1 -1
  43. data/app/views/ahoy_captain/roots/show.html.erb +81 -73
  44. data/config/routes.rb +6 -0
  45. data/lib/ahoy_captain/ahoy/event_methods.rb +28 -74
  46. data/lib/ahoy_captain/goals.rb +9 -1
  47. data/lib/ahoy_captain/version.rb +1 -1
  48. metadata +21 -18
  49. data/app/assets/javascript/ahoy_captain/controllers/filter_controller.js +0 -151
  50. data/app/assets/javascript/ahoy_captain/controllers/link_controller.js +0 -43
  51. data/app/assets/javascript/ahoy_captain/controllers/navigation_controller.js +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be1cf9a1c2b19db594abc06b05d15085bc180fe720986671f9016f1d660feec5
4
- data.tar.gz: e447a70fc694ae39ad8500661a5726eb73ecd4777a0c6d9b4f4bc7d1fdee0541
3
+ metadata.gz: dc0360816004f1a5f8cf41e01f4a55f30b0ff40db97084122678c1b179adf930
4
+ data.tar.gz: a05b89988c2220ca1077bc7eecc80ad1454f777f3eff8621d91b323c52ce6ad7
5
5
  SHA512:
6
- metadata.gz: cef676f367fd05a6b080db63106ab748347b32316f93245a1b1f1bed2bca99a1cb6df185b168721629eae537d14167ef494fc42f6ad1cb24d9eabbd1160ae0f1
7
- data.tar.gz: af29e7be6843d2ceb9922b5e234219c6466e72dda75d992078cf6a95c20e52fe62f6e870f6eaef68d546d00beaf4179222b4bd504eee2666274e571bc158f2d3
6
+ metadata.gz: 1e9806cda9cb8366c5639db6d87fe3983cdd327cff484af95782e8c68134fb9c0f3ed55f8b4b7d269ce41b125bbb5da2faf2f4cf2fc1f190faa5c77bf486e2cc
7
+ data.tar.gz: 3ac41065fd0f767d951785c9a46373318ace8d2c4099390d33ae17f2d39701843a4d8c478dbb8232880d8c9b2cc89bf83a0a7ec7ec6065a52281db41d33e9b70
@@ -0,0 +1,15 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+
3
+ export default class extends Controller {
4
+ static targets = ["link"]
5
+ connect() {
6
+ this.handleLinkClick = (event) => {
7
+ this.linkTargets.forEach(link => link.classList.remove('text-primary', 'font-semibold'))
8
+ event.target.classList.add('text-primary', 'font-semibold')
9
+ }
10
+ this.linkTargets.forEach(link => {
11
+ link.addEventListener('click', this.handleLinkClick)
12
+ })
13
+ }
14
+
15
+ }
@@ -0,0 +1,13 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ handleReset(event) {
5
+ event.preventDefault();
6
+ const openModal = document.querySelector('dialog.modal[open]');
7
+ openModal.querySelectorAll('input, select').forEach(element => {
8
+ element.value = ""
9
+ });
10
+ openModal.close()
11
+ this.element.requestSubmit()
12
+ }
13
+ }
@@ -0,0 +1,37 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+
3
+ export default class extends Controller {
4
+ static values = {
5
+ data: Object,
6
+ label: String
7
+ }
8
+ connect() {
9
+ const getCSS = (varname) => {
10
+ return `hsl(${getComputedStyle(document.documentElement).getPropertyValue(varname)})`
11
+ }
12
+ Chart.register(Chart.Colors)
13
+
14
+ new Chart(this.element,
15
+ {
16
+ type: 'line',
17
+ data: {
18
+ labels: Object.keys(this.dataValue),
19
+ datasets: [
20
+ {
21
+ label: this.labelValue,
22
+ data: Object.values(this.dataValue),
23
+ borderColor: getCSS('--p'),
24
+ backgroundColor: getCSS('--af'),
25
+ color: getCSS('--pc')
26
+ }
27
+ ]
28
+ },
29
+ plugins: {
30
+ colors: {
31
+ forceOverride: true
32
+ }
33
+ }
34
+ },
35
+ )
36
+ }
37
+ }
@@ -0,0 +1,10 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+ import SlimSelect from 'slim-select'
3
+
4
+ export default class extends Controller {
5
+ static targets = ["select"]
6
+
7
+ handleChange(event) {
8
+ this.selectTarget.name = event.target.value
9
+ }
10
+ }
@@ -7,6 +7,8 @@ export default class extends Controller {
7
7
  this.reload = this.reload.bind(this);
8
8
  this.setLabel = this.setLabel.bind(this);
9
9
  this.labelCount = 0;
10
+ this.reloadInterval = setInterval(this.reload, 1000 * 30);
11
+ this.labelInterval = setInterval(this.setLabel, 1000);
10
12
  }
11
13
 
12
14
  reload() {
@@ -0,0 +1,65 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+ import SlimSelect from 'slim-select'
3
+
4
+ export default class extends Controller {
5
+ static values = {
6
+ query: String,
7
+ url: String,
8
+ selected: Array
9
+ }
10
+
11
+ connect() {
12
+ this.loadedInitialData = false;
13
+ this.search = this.search.bind(this)
14
+ this.select = new SlimSelect({
15
+ select: this.element,
16
+ data: [],
17
+ settings: {
18
+ contentPosition: 'relative',
19
+ contentLocation: this.element.closest('fieldset'),
20
+ searchText: 'Sorry, no results found',
21
+ searchPlaceholder: 'Type to populate results',
22
+ placeholderText: `Search`,
23
+ searchHighlight: true
24
+ },
25
+ events: {
26
+ beforeOpen: async () => {
27
+ if (!this.loadedInitialData) {
28
+ const data = await this.search("");
29
+ this.select.setData(data);
30
+ this.loadedInitialData = true
31
+ }
32
+ },
33
+ search: this.search
34
+ }
35
+ });
36
+
37
+ if(this.selectedValue.length) {
38
+ this.select.setData(this.selectedValue.map(item => ({ "text": item, "value": item })))
39
+ this.select.setSelected(this.selectedValue)
40
+ }
41
+ }
42
+
43
+ async search(query) {
44
+ const searchParams = new URLSearchParams(window.location.search);
45
+ const formData = new FormData(this.element.form);
46
+
47
+ let deleted = [];
48
+ for (const [key, value] of formData) {
49
+ if(!deleted.includes(key)) {
50
+ searchParams.delete(key)
51
+ deleted.push(key)
52
+ }
53
+
54
+ searchParams.append(key, value)
55
+ }
56
+
57
+ searchParams.delete(this.element.name);
58
+ searchParams.set(this.queryValue, query);
59
+
60
+ const response = await fetch(`${this.urlValue}?${searchParams.toString()}`);
61
+ const data = await response.json();
62
+ return data;
63
+ }
64
+
65
+ }
@@ -1,13 +1,14 @@
1
1
  <dialog id="<%= id %>" class="modal">
2
- <form method="dialog" class="modal-box w-11/12 max-w-5xl" data-controller='filter' data-action="submit->filter#applyFilters reset->filter#resetFilters">
2
+ <div class="modal-box w-11/12 max-w-5xl">
3
3
  <fieldset>
4
4
  <h5><%= title %></h5>
5
5
  <%= modal_display %>
6
6
  <button class="btn btn-primary mt-4" type="submit">Apply</button>
7
7
  <button class="btn btn-primary mt-4" type="reset">Reset</button>
8
8
  </fieldset>
9
- </form>
10
- <form method="dialog" class="modal-backdrop">
11
- <button>close</button>
12
- </form>
9
+ </div>
10
+ <label class="modal-backdrop">
11
+ <button onclick="event.preventDefault(); <%=id %>.close();">Close</button>
12
+ </label>
13
13
  </dialog>
14
+
@@ -1,21 +1,23 @@
1
- <fieldset class="flex space-x-4 items-end">
1
+ <fieldset class="flex space-x-4 items-end" data-controller="predicate-select">
2
2
  <div class="flex flex-col w-[20%]">
3
3
  <label class="label"><%= label %></label>
4
- <select class='select select-bordered' data-filter-target='predicate' name="<%= column %>">
5
- <% predicates.each do |predicate| %>
6
- <option value="<%= column %>_<%= predicate %>" <%= 'selected' if selected_predicate == "#{column}_#{predicate}" %>>
4
+ <select class='select select-bordered' data-action='change->predicate-select#handleChange'>
5
+ <% predicate_options.each do |predicate| %>
6
+ <option value="<%= option_value(predicate) %>" <%= 'selected' if selected_predicate == predicate %>>
7
7
  <%= predicate.to_s.humanize %>
8
8
  </option>
9
9
  <% end %>
10
10
  </select>
11
11
  </div>
12
+
12
13
  <select
14
+ data-predicate-select-target="select"
15
+ data-controller="search-select"
13
16
  class='w-[50%] border-base-content border-opacity-20 rounded-lg min-h-[3rem]'
14
- name="<%= column %>"
15
- multiple
16
- data-filter-target="select"
17
- data-filter-column-value="<%= column %>"
18
- data-filter-url-value="<%= url %>"
19
- data-filter-selected="<%= values %>"
20
- ></select>
17
+ name="<%= column_name_with_predicate %>"
18
+ <% if multiple %>multiple<% end %>
19
+ data-search-select-query-value="q[<%= column %>_i_cont]"
20
+ data-search-select-url-value="<%= url %>"
21
+ data-search-select-selected-value="<%= values %>"
22
+ ></select>
21
23
  </fieldset>
@@ -1,17 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class AhoyCaptain::Filter::SelectComponent < ViewComponent::Base
4
- def initialize(label:, column:, url:, predicates:)
4
+ def initialize(label:, column:, url:, predicates:, form:, multiple: true)
5
5
  @label = label
6
6
  @column = column
7
7
  @url = url
8
8
  @predicates = predicates
9
+ @form = form
10
+ @multiple = multiple
9
11
  end
10
12
 
13
+ private
14
+
11
15
  def selected_predicate
12
16
  predicate_options.detect { |option| params.dig(:q, option) }
13
17
  end
14
18
 
19
+ def option_value(predicate)
20
+ name = "q[#{predicate}]"
21
+ name += "[]" if multiple
22
+ name
23
+ end
24
+
25
+ def column_name_with_predicate
26
+ if selected_predicate
27
+ option_value(selected_predicate)
28
+ else
29
+ option_value(predicate_options.first)
30
+ end
31
+ end
32
+
15
33
  def values
16
34
  predicate_options.each do |predicate|
17
35
  option = params.dig(:q, predicate)
@@ -23,10 +41,9 @@ class AhoyCaptain::Filter::SelectComponent < ViewComponent::Base
23
41
  []
24
42
  end
25
43
 
26
- private
27
-
28
44
  def predicate_options
29
45
  @predicate_options ||= @predicates.map { |predicate| "#{@column}_#{predicate}" }
30
46
  end
31
- attr_reader :label, :column, :url, :predicates
47
+
48
+ attr_reader :label, :column, :url, :predicates, :form, :multiple
32
49
  end
@@ -1,44 +1,11 @@
1
1
  <div class="flex flex-col min-h-[380px] w-full pt-4">
2
- <div class="flex text-sm font-bold text-base-content mb-4">
3
- <span class="grow"><%= category_name %></span>
4
- <span ><%= unit_name %></span>
5
- <% if additional_cols.include?(:percent_total) %>
6
- <span class="w-8 ml-8 text-right">%</span>
7
- <% end %>
8
- <% if additional_cols.include?(:total) %>
9
- <span class="w-8 ml-8 text-right">Total</span>
10
- <% end %>
11
- <% if additional_cols.include?(:conversion_rate) %>
12
- <span class="w-8 ml-8 text-right">CR</span>
13
- <% end %>
14
- </div>
2
+ <%= render @header %>
15
3
  <div class='min-h-[420px]'>
16
4
  <div class="grow">
17
5
  <% if items.respond_to?(:each) && items.any? %>
18
6
  <% items.each do |item| %>
19
7
  <div class='leading-10 flex relative'>
20
- <progress class='progress-primary bg-base-100 h-8 grow' value="<%= item.unit_amount %>" max="<%= max_amount %>">
21
- </progress>
22
- <span class="grow text-elipsis overflow-hidden absolute left-4 bottom-3 h-8 text-base-content">
23
- <%= item.display_name %>
24
- </span>
25
- <span class="w-8 ml-8 text-right">
26
- <%= render AhoyCaptain::TooltipComponent.new(amount: item.unit_amount) %>
27
- </span>
28
-
29
- <% if additional_cols.include?(:percent_total) %>
30
- <span class="w-8 ml-8 text-right"><%= percent_total(item) %></span>
31
- <% end %>
32
- <% if additional_cols.include?(:total) %>
33
- <span class="w-8 ml-8 text-right">
34
- <%= render AhoyCaptain::TooltipComponent.new(amount: item.total) %>
35
- </span>
36
- <% end %>
37
- <% if additional_cols.include?(:conversion_rate) %>
38
- <span class="w-8 ml-8 text-right">
39
- <%= item.conversion_rate * 100.0 %>%
40
- </span>
41
- <% end %>
8
+ <%= render_row(item) %>
42
9
  </div>
43
10
  <% end %>
44
11
  <% else %>
@@ -1,13 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class AhoyCaptain::TableComponent < ViewComponent::Base
4
- SUPPORTED_COLS = [:percent_total, :total, :conversion_rate]
4
+ DEFAULT_HEADER = AhoyCaptain::Tables::Headers::HeaderComponent
5
+ DEFAULT_ROW = AhoyCaptain::Tables::Rows::RowComponent
5
6
 
6
- def initialize(items:, category_name:, unit_name:, additional_cols: [])
7
+ def initialize(items:, category_name: nil, unit_name: nil, header: nil, row: DEFAULT_ROW)
7
8
  @items = items
8
9
  @category_name = category_name
9
10
  @unit_name = unit_name
10
11
  @additional_cols = additional_cols
12
+ if header.nil?
13
+ @header = DEFAULT_HEADER.new(category_name: category_name, unit_name: unit_name)
14
+ else
15
+ @header = header.new
16
+ end
17
+ @row = row
18
+ end
19
+
20
+ def render_row(item)
21
+ @row.new(table: self, item: item).render_in(view_context)
11
22
  end
12
23
 
13
24
  private
@@ -22,7 +33,4 @@ class AhoyCaptain::TableComponent < ViewComponent::Base
22
33
  @total ||= items.first.total_count
23
34
  end
24
35
 
25
- def percent_total(item)
26
- '%.1f' % ((item.unit_amount.to_i * 1.0 / total)*100.0)
27
- end
28
36
  end
@@ -0,0 +1,3 @@
1
+ <%= render ::AhoyCaptain::Tables::Headers::HeaderComponent.new(category_name: "Device", unit_name: "Visitors") do %>
2
+ <span class="w-8 ml-8 text-right">%</span>
3
+ <% end %>
@@ -0,0 +1,9 @@
1
+ module AhoyCaptain
2
+ module Tables
3
+ module Headers
4
+ class DevicesHeaderComponent < ViewComponent::Base
5
+
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ <div class="flex text-sm font-bold text-base-content mb-4">
2
+ <span class="grow">Goal</span>
3
+ <span class="w-8 ml-8 text-right">Uniques</span>
4
+ <span class="w-8 ml-8 text-right">Total</span>
5
+ <span class="w-8 ml-8 text-right">CR</span>
6
+ </div>
@@ -0,0 +1,9 @@
1
+ module AhoyCaptain
2
+ module Tables
3
+ module Headers
4
+ class GoalsHeaderComponent < ViewComponent::Base
5
+
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ <div class="flex text-sm font-bold text-base-content mb-4">
2
+ <span class="grow"><%= @category_name %></span>
3
+ <span ><%= @unit_name %></span>
4
+ <%= content %>
5
+ </div>
@@ -0,0 +1,12 @@
1
+ module AhoyCaptain
2
+ module Tables
3
+ module Headers
4
+ class HeaderComponent < ViewComponent::Base
5
+ def initialize(category_name:, unit_name:)
6
+ @category_name = category_name
7
+ @unit_name = unit_name
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ <%= progress_bar(@item.count, @item.total_count, @item.display_name) %>
2
+ <%= item do %>
3
+ <%= tooltip(@item.count) %>
4
+ <% end %>
5
+ <%= item(percent_total(@item)) %>
@@ -0,0 +1,12 @@
1
+ module AhoyCaptain
2
+ module Tables
3
+ module Rows
4
+ class DevicesRowComponent < RowComponent
5
+
6
+ def total
7
+ @item.total_count
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ <%= progress_bar(@item.cr, 100, @item.name) %>
2
+ <%= item do %>
3
+ <%= tooltip(@item.unique_visits) %>
4
+ <% end %>
5
+ <%= item do %>
6
+ <%= tooltip(@item.total_events) %>
7
+ <% end %>
8
+ <%= item do %>
9
+ <%= tooltip(@item.cr) %>
10
+ <% end %>
11
+
@@ -0,0 +1,12 @@
1
+ module AhoyCaptain
2
+ module Tables
3
+ module Rows
4
+ class GoalsRowComponent < RowComponent
5
+
6
+ def total
7
+ @item.total_count
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ <%= progress_bar(@item.count, @item.total_count, @item.display_name) %>
2
+ <%=
3
+ item do
4
+ tooltip(@item.count)
5
+ end
6
+ %>
@@ -0,0 +1,41 @@
1
+ module AhoyCaptain
2
+ module Tables
3
+ module Rows
4
+ class RowComponent < ViewComponent::Base
5
+ def initialize(table:, item:)
6
+ @table = table
7
+ @item = item
8
+ end
9
+
10
+ def progress_bar(value, max, label)
11
+ items = []
12
+ items << view_context.content_tag(:progress, "", class: "progress-primary bg-base-100 h-8 grow", value: value, max: max)
13
+ items << view_context.content_tag(:span, class: "grow text-elipsis overflow-hidden absolute left-4 bottom-3 h-8 text-base-content") do
14
+ label
15
+ end
16
+
17
+ items.join.html_safe
18
+ end
19
+
20
+ def item(value = nil, &block)
21
+ view_context.content_tag(:span, class: "w-8 ml-8 text-right") do
22
+ if value
23
+ value
24
+ else
25
+ capture(&block)
26
+ end
27
+ end
28
+ end
29
+
30
+
31
+ def percent_total(item)
32
+ '%.1f' % ((item.unit_amount.to_i * 1.0 / total)*100.0)
33
+ end
34
+
35
+ def tooltip(value)
36
+ AhoyCaptain::TooltipComponent.new(amount: value).render_in(self)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,10 +1,10 @@
1
1
  module AhoyCaptain
2
2
  module Filters
3
3
  module Pages
4
- # TODO: ACCOMODATE EXIT_PAGES
5
4
  class EntryPagesController < BaseController
6
5
  def index
7
- query = event_query.all.with_url.distinct_url
6
+ query = event_query.all.distinct("entry_pages.url").select("entry_pages.url as url")
7
+
8
8
  render json: query.map { |row| { text: row.url } }
9
9
  end
10
10
 
@@ -1,10 +1,9 @@
1
1
  module AhoyCaptain
2
2
  module Filters
3
3
  module Pages
4
- # TODO: ACCOMODATE ENTRY_PAGES
5
4
  class ExitPagesController < BaseController
6
5
  def index
7
- query = event_query.with_url.distinct_url
6
+ query = event_query.distinct("exit_pages.url").select("exit_pages.url as url")
8
7
 
9
8
  render json: query.map { |row| { text: row.url } }
10
9
  end
@@ -0,0 +1,11 @@
1
+ module AhoyCaptain
2
+ module Filters
3
+ module Properties
4
+ class NamesController < BaseController
5
+ def index
6
+ render json: ::Ahoy::Event.select("jsonb_object_keys(properties) as keys").distinct("jsonb_object_keys(properties)").map(&:keys).map { |key| serialize(key) }
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module AhoyCaptain
2
+ module Filters
3
+ module Properties
4
+ class ValuesController < BaseController
5
+ def index
6
+ query = ::Ahoy::Event.with(elements: event_query.select("ahoy_events.properties->>'controller' as element"))
7
+ .select("distinct elements.element").from("elements")
8
+
9
+
10
+ render json: query.map(&:element).map { |element| serialize(element) }
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -5,6 +5,7 @@ module AhoyCaptain
5
5
  def index
6
6
  @stats = AhoyCaptain::Stats::BounceRatesQuery.call(params)
7
7
  @stats = @stats.group_by_period(selected_interval, "daily_bounce_rate.date").average("bounce_rate")
8
+ @label = "Bounce Rate"
8
9
  end
9
10
  end
10
11
  end
@@ -3,6 +3,7 @@ module AhoyCaptain
3
3
  class TotalPageviewsController < BaseController
4
4
  def index
5
5
  @stats = AhoyCaptain::Stats::TotalPageviewsQuery.call(params).group_by_period(selected_interval, :time).count
6
+ @label = "Visitors"
6
7
  end
7
8
  end
8
9
  end
@@ -3,6 +3,7 @@ module AhoyCaptain
3
3
  class TotalVisitsController < BaseController
4
4
  def index
5
5
  @stats = AhoyCaptain::Stats::TotalVisitorsQuery.call(params).group_by_period(selected_interval, :started_at).count
6
+ @label = "Visitors"
6
7
  end
7
8
  end
8
9
  end
@@ -3,6 +3,7 @@ module AhoyCaptain
3
3
  class UniqueVisitorsController < BaseController
4
4
  def index
5
5
  @stats = AhoyCaptain::Stats::UniqueVisitorsQuery.call(params).group_by_period(selected_interval, :started_at).count
6
+ @label = "Visitors"
6
7
  end
7
8
  end
8
9
  end
@@ -11,6 +11,9 @@ module AhoyCaptain
11
11
  end
12
12
  end
13
13
  end
14
+
15
+ @label = "Views"
16
+
14
17
  end
15
18
  end
16
19
  end
@@ -3,6 +3,7 @@ module AhoyCaptain
3
3
  class VisitDurationsController < BaseController
4
4
  def index
5
5
  @stats = AhoyCaptain::Stats::VisitDurationQuery.call(params).group_by_period(selected_interval, 'started_at').average(:duration)
6
+ @label = "Duration"
6
7
  end
7
8
  end
8
9
  end
@@ -53,6 +53,7 @@ module AhoyCaptain
53
53
  EventQuery.call(params)
54
54
  end
55
55
 
56
+ # this could be better
56
57
  def ransack_params_for(type)
57
58
  ransackable_params = {}
58
59
 
@@ -61,12 +62,13 @@ module AhoyCaptain
61
62
  visit: (AhoyCaptain.visit.ransackable_attributes + AhoyCaptain.visit.ransackable_scopes).map(&:to_s),
62
63
  event: (AhoyCaptain.event.ransackable_attributes + AhoyCaptain.event.ransackable_scopes).map(&:to_s),
63
64
  }
65
+
64
66
  pattern = /(?:_not_eq|_eq|_in|_not_in|_cont|_not_cont|_i_cont)$/
65
67
  params[:q].each do |key, value|
66
68
  attribute_name = key.gsub(pattern, '')
67
- if type == :event && ransackable_attributes[:visit].include?(attribute_name) || ransackable_attributes[:visit].include?(key)
69
+ if type == :event && (ransackable_attributes[:visit].include?(attribute_name) || ransackable_attributes[:visit].include?(key))
68
70
  ransackable_params["visit_#{key}"] = value
69
- elsif type == :visit && ransackable_attributes[:event].include?(attribute_name) || ransackable_attributes[:event].include?(key)
71
+ elsif type == :visit && (ransackable_attributes[:event].include?(attribute_name) || ransackable_attributes[:event].include?(key))
70
72
  ransackable_params["events_#{key}"] = value
71
73
  else
72
74
  ransackable_params[key] = value
@@ -102,7 +104,6 @@ module AhoyCaptain
102
104
  end
103
105
  end
104
106
 
105
-
106
107
  ransackable_params
107
108
  end
108
109