rails_analytics 1.1.1 → 1.2.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.
- checksums.yaml +4 -4
- data/README.md +6 -0
- data/app/assets/stylesheets/rails_analytics/dashboard.css +101 -0
- data/app/controllers/rails_analytics/journeys_controller.rb +12 -0
- data/app/models/rails_analytics/journey.rb +111 -0
- data/app/views/layouts/rails_analytics.html.erb +1 -0
- data/app/views/rails_analytics/dashboards/events.html.erb +5 -3
- data/app/views/rails_analytics/dashboards/overview.html.erb +7 -1
- data/app/views/rails_analytics/journeys/index.html.erb +62 -0
- data/config/locales/rails_analytics.en.yml +23 -1
- data/config/locales/rails_analytics.pt-BR.yml +23 -1
- data/config/routes.rb +1 -0
- data/lib/rails_analytics/stats.rb +15 -0
- data/lib/rails_analytics/version.rb +1 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 235ecc86f0e45e6c0989fbe9a5bc7b3623ab9f5b1f38ede89d991d938c6b6235
|
|
4
|
+
data.tar.gz: 7e46192a35e21e6e255b6f87f09329ddd74d1b2bcd177beaa5063161d4361006
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b30ba06c428da41976626821312671a5c66f8463522b5e35bdef6355b60762deb06b33ba51b518e1381378caf083ec418e378511d358e9c0d402d1ca316e9382
|
|
7
|
+
data.tar.gz: d06bdbee94eb980257e973ed7d9c7158af4203b8d907af502f56c15935c592cbaa8f6d3cb39e00e0f7cc6a105695fc478211a7ef4c07d5e7e032b985acab4c70
|
data/README.md
CHANGED
|
@@ -9,6 +9,12 @@ The dashboard's chart shows CSS-only tooltips: hover (or keyboard-focus) any
|
|
|
9
9
|
data point to see its date and visit count — labels on the x-axis mark first,
|
|
10
10
|
last and month boundaries. No JavaScript anywhere.
|
|
11
11
|
|
|
12
|
+
The **Journeys** view lists recent sessions as cards: each card shows the
|
|
13
|
+
user's ordered trajectory (landing page → clicks → scroll depth → forms) with
|
|
14
|
+
arrows between steps, hover/focus details on every step, and origin, device
|
|
15
|
+
and UTM in the card header. Filter by event name to see the journeys that
|
|
16
|
+
contain a specific click/topic.
|
|
17
|
+
|
|
12
18
|
## Screenshots
|
|
13
19
|
|
|
14
20
|
| Dashboard — aggregated overview | Dashboard — events & visits |
|
|
@@ -139,6 +139,107 @@ body {
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
/* Journey cards */
|
|
143
|
+
.ra-journey {
|
|
144
|
+
border: 1px solid var(--ra-border);
|
|
145
|
+
border-radius: var(--ra-radius);
|
|
146
|
+
margin-bottom: 16px;
|
|
147
|
+
overflow: hidden;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.ra-journey-head {
|
|
151
|
+
display: flex;
|
|
152
|
+
align-items: center;
|
|
153
|
+
gap: 8px;
|
|
154
|
+
padding: 10px 14px;
|
|
155
|
+
background: var(--ra-accent-soft);
|
|
156
|
+
border-bottom: 1px solid var(--ra-border);
|
|
157
|
+
font-size: 12px;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.ra-journey-origin {
|
|
161
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
162
|
+
font-weight: 700;
|
|
163
|
+
}
|
|
164
|
+
.ra-journey-origin:empty::before { content: "direct"; }
|
|
165
|
+
|
|
166
|
+
.ra-journey-meta { color: var(--ra-muted); }
|
|
167
|
+
.ra-journey-utm {
|
|
168
|
+
background: var(--ra-accent);
|
|
169
|
+
color: #fff;
|
|
170
|
+
border-radius: 10px;
|
|
171
|
+
padding: 1px 8px;
|
|
172
|
+
font-size: 11px;
|
|
173
|
+
}
|
|
174
|
+
.ra-journey-steps { margin-left: auto; color: var(--ra-muted); font-variant-numeric: tabular-nums; }
|
|
175
|
+
|
|
176
|
+
.ra-journey-path {
|
|
177
|
+
display: flex;
|
|
178
|
+
flex-wrap: wrap;
|
|
179
|
+
gap: 4px;
|
|
180
|
+
align-items: center;
|
|
181
|
+
padding: 14px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.ra-journey-arrow {
|
|
185
|
+
color: var(--ra-muted);
|
|
186
|
+
font-size: 12px;
|
|
187
|
+
flex: none;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* Step chip. Tooltip is CSS-only: shown on :hover and :focus. */
|
|
191
|
+
.ra-journey-step {
|
|
192
|
+
position: relative;
|
|
193
|
+
display: inline-flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
border: 1px solid var(--ra-border);
|
|
196
|
+
border-radius: 999px;
|
|
197
|
+
padding: 4px 12px;
|
|
198
|
+
background: var(--ra-card);
|
|
199
|
+
cursor: default;
|
|
200
|
+
outline: none;
|
|
201
|
+
}
|
|
202
|
+
.ra-journey-step:focus-visible { border-color: var(--ra-accent); }
|
|
203
|
+
|
|
204
|
+
.ra-journey-step:focus-visible,
|
|
205
|
+
.ra-journey-step:focus:not(:focus-visible) {
|
|
206
|
+
border-color: var(--ra-accent);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.ra-journey-step--click { border-color: var(--ra-accent); color: var(--ra-accent); font-weight: 600; }
|
|
210
|
+
.ra-journey-step--outbound { border-style: dashed; color: var(--ra-muted); }
|
|
211
|
+
.ra-journey-step--form { border-color: #059669; color: #059669; }
|
|
212
|
+
.ra-journey-step--scroll { color: var(--ra-muted); }
|
|
213
|
+
.ra-journey-step--email { border-color: #d97706; color: #d97706; }
|
|
214
|
+
.ra-journey-step--pageview { background: var(--ra-bg); font-weight: 600; }
|
|
215
|
+
|
|
216
|
+
.ra-journey-step-detail {
|
|
217
|
+
position: absolute;
|
|
218
|
+
bottom: calc(100% + 8px);
|
|
219
|
+
left: 50%;
|
|
220
|
+
transform: translateX(-50%);
|
|
221
|
+
min-width: 140px;
|
|
222
|
+
max-width: 260px;
|
|
223
|
+
background: var(--ra-text);
|
|
224
|
+
color: var(--ra-card);
|
|
225
|
+
border-radius: 8px;
|
|
226
|
+
padding: 6px 10px;
|
|
227
|
+
font-size: 12px;
|
|
228
|
+
line-height: 1.5;
|
|
229
|
+
display: flex;
|
|
230
|
+
flex-direction: column;
|
|
231
|
+
gap: 2px;
|
|
232
|
+
opacity: 0;
|
|
233
|
+
pointer-events: none;
|
|
234
|
+
transition: opacity 0.12s ease;
|
|
235
|
+
z-index: 10;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.ra-journey-step:hover .ra-journey-step-detail,
|
|
239
|
+
.ra-journey-step:focus .ra-journey-step-detail { opacity: 1; }
|
|
240
|
+
|
|
241
|
+
.ra-journey-step-time { font-weight: 700; opacity: 0.75; }
|
|
242
|
+
|
|
142
243
|
/* Pagination */
|
|
143
244
|
.ra-pagination {
|
|
144
245
|
display: flex;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsAnalytics
|
|
4
|
+
class JourneysController < ApplicationController
|
|
5
|
+
def index
|
|
6
|
+
@event_name = params[:event]
|
|
7
|
+
@page = (params[:page] || 1).to_i
|
|
8
|
+
@journeys = Stats.journeys(event: @event_name.presence, page: @page)
|
|
9
|
+
@event_names = Stats.event_counts_by_name.keys.sort
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsAnalytics
|
|
4
|
+
# Serializes a Visit (session) into a user journey: ordered steps with
|
|
5
|
+
# kind/label/detail, plus origin and context metadata for the card header.
|
|
6
|
+
class Journey
|
|
7
|
+
def initialize(visit)
|
|
8
|
+
@visit = visit
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_h
|
|
12
|
+
{
|
|
13
|
+
id: @visit.id,
|
|
14
|
+
origin: origin,
|
|
15
|
+
device: @visit.device_type,
|
|
16
|
+
viewport: @visit.viewport,
|
|
17
|
+
language: @visit.language,
|
|
18
|
+
utm: utm,
|
|
19
|
+
started_at: @visit.started_at,
|
|
20
|
+
duration: duration,
|
|
21
|
+
steps: steps
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def origin
|
|
28
|
+
@visit.referrer_domain.presence || "direct"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def utm
|
|
32
|
+
{
|
|
33
|
+
source: @visit.utm_source,
|
|
34
|
+
campaign: @visit.utm_campaign,
|
|
35
|
+
medium: @visit.utm_medium
|
|
36
|
+
}.compact
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def duration
|
|
40
|
+
last = @visit.events.map(&:time).max || @visit.started_at
|
|
41
|
+
(last - @visit.started_at).to_i
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Ordered: landing page first, then tracked events by time.
|
|
45
|
+
def steps
|
|
46
|
+
sorted = @visit.events.sort_by(&:time)
|
|
47
|
+
[pageview_step] + sorted.map { |event| event_step(event) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def pageview_step
|
|
51
|
+
{
|
|
52
|
+
kind: :pageview,
|
|
53
|
+
label: @visit.landing_page_path.to_s.split("?").first.presence || "/",
|
|
54
|
+
detail: pageview_detail,
|
|
55
|
+
time: @visit.started_at
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def pageview_detail
|
|
60
|
+
parts = [I18n.t("rails_analytics.journeys.devices.#{@visit.device_type}", default: @visit.device_type)]
|
|
61
|
+
parts << I18n.t("rails_analytics.journeys.details.viewport", value: @visit.viewport) if @visit.viewport.present?
|
|
62
|
+
parts << I18n.t("rails_analytics.journeys.details.language", value: @visit.language) if @visit.language.present?
|
|
63
|
+
parts.join(" · ")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def event_step(event)
|
|
67
|
+
props = event.properties.is_a?(Hash) ? event.properties.symbolize_keys : {}
|
|
68
|
+
kind = kind_for(event.name)
|
|
69
|
+
|
|
70
|
+
{
|
|
71
|
+
kind: kind,
|
|
72
|
+
label: label_for(event.name, kind, props),
|
|
73
|
+
detail: detail_for(props),
|
|
74
|
+
time: event.time
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def kind_for(name)
|
|
79
|
+
case name
|
|
80
|
+
when /outbound/ then :outbound
|
|
81
|
+
when /email/ then :email
|
|
82
|
+
when /scroll/ then :scroll
|
|
83
|
+
when /form/ then :form
|
|
84
|
+
when /time/ then :time
|
|
85
|
+
when /click/ then :click
|
|
86
|
+
else :other
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def label_for(name, kind, props)
|
|
91
|
+
case kind
|
|
92
|
+
when :click then props[:value].presence || name
|
|
93
|
+
when :scroll then I18n.t("rails_analytics.journeys.steps.scroll", mark: props[:mark].to_i)
|
|
94
|
+
when :outbound then props[:target].presence || name
|
|
95
|
+
when :email then I18n.t("rails_analytics.journeys.steps.email")
|
|
96
|
+
when :form then I18n.t("rails_analytics.journeys.steps.form", form: props[:form].to_s)
|
|
97
|
+
when :time then I18n.t("rails_analytics.journeys.steps.time_on_page", seconds: props[:seconds].to_i)
|
|
98
|
+
else props[:value].presence || name
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def detail_for(props)
|
|
103
|
+
return if props.empty?
|
|
104
|
+
|
|
105
|
+
props
|
|
106
|
+
.reject { |_, value| value.blank? }
|
|
107
|
+
.map { |key, value| "#{key}: #{value}" }
|
|
108
|
+
.join(" · ")
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
<%= link_to t("rails_analytics.nav.overview"), rails_analytics.root_path %>
|
|
15
15
|
<%= link_to t("rails_analytics.nav.events"), rails_analytics.events_path %>
|
|
16
16
|
<%= link_to t("rails_analytics.nav.visits"), rails_analytics.visits_path %>
|
|
17
|
+
<%= link_to t("rails_analytics.nav.journeys"), rails_analytics.journeys_path %>
|
|
17
18
|
</nav>
|
|
18
19
|
</header>
|
|
19
20
|
<main class="ra-main">
|
|
@@ -28,9 +28,11 @@
|
|
|
28
28
|
<% @events[:data].each do |row| %>
|
|
29
29
|
<tr>
|
|
30
30
|
<td class="ra-mono"><%= row[:date] %></td>
|
|
31
|
-
<td class="ra-mono"
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
<td class="ra-mono">
|
|
32
|
+
<%= row[:name] %>
|
|
33
|
+
<%= link_to t("rails_analytics.journeys.view"), rails_analytics.journeys_path(event: row[:name]) %>
|
|
34
|
+
</td>
|
|
35
|
+
<td class="ra-num"><%= number_with_delimiter(row[:count]) %></td> </tr>
|
|
34
36
|
<% end %>
|
|
35
37
|
</tbody>
|
|
36
38
|
</table>
|
|
@@ -53,7 +53,13 @@
|
|
|
53
53
|
</thead>
|
|
54
54
|
<tbody>
|
|
55
55
|
<% @stats[:top_events].each do |name, count| %>
|
|
56
|
-
<tr
|
|
56
|
+
<tr>
|
|
57
|
+
<td class="ra-mono">
|
|
58
|
+
<%= name %>
|
|
59
|
+
<%= link_to t("rails_analytics.journeys.view"), rails_analytics.journeys_path(event: name) %>
|
|
60
|
+
</td>
|
|
61
|
+
<td class="ra-num"><%= number_with_delimiter(count) %></td>
|
|
62
|
+
</tr>
|
|
57
63
|
<% end %>
|
|
58
64
|
</tbody>
|
|
59
65
|
</table>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<%# app/views/rails_analytics/journeys/index.html.erb %>
|
|
2
|
+
<% fmt = t("rails_analytics.journeys.datetime_format") %>
|
|
3
|
+
<section class="ra-card">
|
|
4
|
+
<h2 class="ra-card-title"><%= t("rails_analytics.journeys.title") %></h2>
|
|
5
|
+
|
|
6
|
+
<div class="ra-filter">
|
|
7
|
+
<%= form_tag rails_analytics.journeys_path, method: :get do %>
|
|
8
|
+
<%= select_tag :event,
|
|
9
|
+
options_for_select(
|
|
10
|
+
[[t("rails_analytics.filter.all_events"), ""]] +
|
|
11
|
+
@event_names.map { |n| [n, n] },
|
|
12
|
+
@event_name
|
|
13
|
+
),
|
|
14
|
+
onchange: "this.form.submit()" %>
|
|
15
|
+
<noscript><%= submit_tag t("rails_analytics.filter.filter") %></noscript>
|
|
16
|
+
<% end %>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<% if @journeys[:data].any? %>
|
|
20
|
+
<% @journeys[:data].each do |journey| %>
|
|
21
|
+
<% j = journey.to_h %>
|
|
22
|
+
<article class="ra-journey">
|
|
23
|
+
<header class="ra-journey-head">
|
|
24
|
+
<span class="ra-journey-origin" title="<%= t("rails_analytics.journeys.origin") %>"><%= j[:origin] %></span>
|
|
25
|
+
<span class="ra-journey-meta"><%= j[:device] %></span>
|
|
26
|
+
<% if j[:utm][:source].present? %>
|
|
27
|
+
<span class="ra-journey-utm" title="<%= j[:utm].map { |k, v| "#{k}: #{v}" }.join(', ') %>"><%= j[:utm][:source] %></span>
|
|
28
|
+
<% end %>
|
|
29
|
+
<span class="ra-journey-steps">
|
|
30
|
+
<%= t("rails_analytics.journeys.step_count", count: j[:steps].size) %>
|
|
31
|
+
</span>
|
|
32
|
+
<time class="ra-journey-steps"><%= j[:started_at].strftime(fmt) %></time>
|
|
33
|
+
</header>
|
|
34
|
+
|
|
35
|
+
<div class="ra-journey-path">
|
|
36
|
+
<% j[:steps].each_with_index do |step, i| %>
|
|
37
|
+
<% if i > 0 %><span class="ra-journey-arrow" aria-hidden="true">→</span><% end %>
|
|
38
|
+
<span class="ra-journey-step ra-journey-step--<%= step[:kind] %>" tabindex="0">
|
|
39
|
+
<span class="ra-journey-step-label"><%= step[:label] %></span>
|
|
40
|
+
<span class="ra-journey-step-detail" role="tooltip">
|
|
41
|
+
<span class="ra-journey-step-time"><%= step[:time].strftime(fmt) if step[:time] %></span>
|
|
42
|
+
<% if step[:detail].present? %><span><%= step[:detail] %></span><% end %>
|
|
43
|
+
</span>
|
|
44
|
+
</span>
|
|
45
|
+
<% end %>
|
|
46
|
+
</div>
|
|
47
|
+
</article>
|
|
48
|
+
<% end %>
|
|
49
|
+
|
|
50
|
+
<div class="ra-pagination">
|
|
51
|
+
<% if @page > 1 %>
|
|
52
|
+
<%= link_to t("rails_analytics.pagination.previous"), rails_analytics.journeys_path(page: @page - 1, event: @event_name) %>
|
|
53
|
+
<% end %>
|
|
54
|
+
<span class="current"><%= @page %></span>
|
|
55
|
+
<% if @journeys[:data].length == @journeys[:per_page] %>
|
|
56
|
+
<%= link_to t("rails_analytics.pagination.next"), rails_analytics.journeys_path(page: @page + 1, event: @event_name) %>
|
|
57
|
+
<% end %>
|
|
58
|
+
</div>
|
|
59
|
+
<% else %>
|
|
60
|
+
<p class="ra-empty"><%= t("rails_analytics.empty") %></p>
|
|
61
|
+
<% end %>
|
|
62
|
+
</section>
|
|
@@ -5,6 +5,7 @@ en:
|
|
|
5
5
|
overview: "Overview"
|
|
6
6
|
events: "Events"
|
|
7
7
|
visits: "Visits"
|
|
8
|
+
journeys: "Journeys"
|
|
8
9
|
footer: "Aggregated and anonymous data — 6 month retention"
|
|
9
10
|
kpi:
|
|
10
11
|
visits: "Visits"
|
|
@@ -38,4 +39,25 @@ en:
|
|
|
38
39
|
filter: "Filter"
|
|
39
40
|
pagination:
|
|
40
41
|
previous: "Previous"
|
|
41
|
-
next: "Next"
|
|
42
|
+
next: "Next"
|
|
43
|
+
journeys:
|
|
44
|
+
view: "journeys"
|
|
45
|
+
title: "Journeys by session"
|
|
46
|
+
datetime_format: "%m/%d %H:%M"
|
|
47
|
+
origin: "Origin"
|
|
48
|
+
step_count:
|
|
49
|
+
one: "1 step"
|
|
50
|
+
other: "%{count} steps"
|
|
51
|
+
steps:
|
|
52
|
+
scroll: "Scroll %{mark}%"
|
|
53
|
+
form: "Form %{form}"
|
|
54
|
+
email: "Email"
|
|
55
|
+
time_on_page: "%{seconds}s on page"
|
|
56
|
+
devices:
|
|
57
|
+
desktop: "Desktop"
|
|
58
|
+
mobile: "Mobile"
|
|
59
|
+
tablet: "Tablet"
|
|
60
|
+
unknown: "Unknown"
|
|
61
|
+
details:
|
|
62
|
+
viewport: "Screen %{value}"
|
|
63
|
+
language: "Language %{value}"
|
|
@@ -5,6 +5,7 @@ pt-BR:
|
|
|
5
5
|
overview: "Visão geral"
|
|
6
6
|
events: "Eventos"
|
|
7
7
|
visits: "Visitas"
|
|
8
|
+
journeys: "Trajetórias"
|
|
8
9
|
footer: "Dados agregados e anônimos — retenção 6 meses"
|
|
9
10
|
kpi:
|
|
10
11
|
visits: "Visitas"
|
|
@@ -38,4 +39,25 @@ pt-BR:
|
|
|
38
39
|
filter: "Filtrar"
|
|
39
40
|
pagination:
|
|
40
41
|
previous: "Anterior"
|
|
41
|
-
next: "Próximo"
|
|
42
|
+
next: "Próximo"
|
|
43
|
+
journeys:
|
|
44
|
+
view: "trajetórias"
|
|
45
|
+
title: "Trajetórias por sessão"
|
|
46
|
+
datetime_format: "%d/%m %H:%M"
|
|
47
|
+
origin: "Origem"
|
|
48
|
+
step_count:
|
|
49
|
+
one: "1 passo"
|
|
50
|
+
other: "%{count} passos"
|
|
51
|
+
steps:
|
|
52
|
+
scroll: "Scroll %{mark}%"
|
|
53
|
+
form: "Form %{form}"
|
|
54
|
+
email: "E-mail"
|
|
55
|
+
time_on_page: "%{seconds}s na página"
|
|
56
|
+
devices:
|
|
57
|
+
desktop: "Desktop"
|
|
58
|
+
mobile: "Mobile"
|
|
59
|
+
tablet: "Tablet"
|
|
60
|
+
unknown: "Desconhecido"
|
|
61
|
+
details:
|
|
62
|
+
viewport: "Tela %{value}"
|
|
63
|
+
language: "Idioma %{value}"
|
data/config/routes.rb
CHANGED
|
@@ -139,6 +139,21 @@ module RailsAnalytics
|
|
|
139
139
|
Visit.since(since)
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
+
# Sessions with their events, newest first. Optional filter by event name.
|
|
143
|
+
def journeys(event: nil, page: 1, per_page: 10, since: default_since)
|
|
144
|
+
scope = base_scope(since)
|
|
145
|
+
if event.present?
|
|
146
|
+
scope = scope.joins(:events).merge(Event.named(event))
|
|
147
|
+
end
|
|
148
|
+
visits = scope.order(started_at: :desc)
|
|
149
|
+
.offset((page - 1) * per_page)
|
|
150
|
+
.limit(per_page)
|
|
151
|
+
.includes(:events)
|
|
152
|
+
|
|
153
|
+
{ data: visits.map { |visit| Journey.new(visit) },
|
|
154
|
+
total: scope.count, page: page, per_page: per_page }
|
|
155
|
+
end
|
|
156
|
+
|
|
142
157
|
def default_since
|
|
143
158
|
RailsAnalytics.config.since_default.ago
|
|
144
159
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails_analytics
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- br4zz4
|
|
@@ -40,16 +40,19 @@ files:
|
|
|
40
40
|
- app/controllers/rails_analytics/analytics_controller.rb
|
|
41
41
|
- app/controllers/rails_analytics/application_controller.rb
|
|
42
42
|
- app/controllers/rails_analytics/dashboards_controller.rb
|
|
43
|
+
- app/controllers/rails_analytics/journeys_controller.rb
|
|
43
44
|
- app/helpers/rails_analytics/application_helper.rb
|
|
44
45
|
- app/jobs/rails_analytics/retention_job.rb
|
|
45
46
|
- app/models/rails_analytics/chart_builder.rb
|
|
46
47
|
- app/models/rails_analytics/daily_salt.rb
|
|
47
48
|
- app/models/rails_analytics/event.rb
|
|
49
|
+
- app/models/rails_analytics/journey.rb
|
|
48
50
|
- app/models/rails_analytics/visit.rb
|
|
49
51
|
- app/views/layouts/rails_analytics.html.erb
|
|
50
52
|
- app/views/rails_analytics/dashboards/events.html.erb
|
|
51
53
|
- app/views/rails_analytics/dashboards/overview.html.erb
|
|
52
54
|
- app/views/rails_analytics/dashboards/visits.html.erb
|
|
55
|
+
- app/views/rails_analytics/journeys/index.html.erb
|
|
53
56
|
- config/locales/rails_analytics.en.yml
|
|
54
57
|
- config/locales/rails_analytics.pt-BR.yml
|
|
55
58
|
- config/routes.rb
|