spree_google_analytics 1.0.0 → 1.1.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 +43 -2
- data/app/assets/config/spree_google_analytics_manifest.js +4 -0
- data/app/controllers/spree_google_analytics/store_controller_decorator.rb +11 -0
- data/app/helpers/spree_google_analytics/base_helper.rb +188 -40
- data/app/javascript/spree_google_analytics/application.js +16 -0
- data/app/javascript/spree_google_analytics/controllers/spree_google_analytics_controller.js +4 -0
- data/app/models/spree/integrations/google_analytics.rb +5 -1
- data/app/views/spree/admin/integrations/forms/_google_analytics.html.erb +17 -11
- data/app/views/spree_google_analytics/_add_to_cart.html.erb +10 -4
- data/app/views/spree_google_analytics/_add_to_wishlist.erb +10 -4
- data/app/views/spree_google_analytics/_body_end.html.erb +50 -16
- data/app/views/spree_google_analytics/_body_start.html.erb +6 -0
- data/app/views/spree_google_analytics/_cart.html.erb +14 -6
- data/app/views/spree_google_analytics/_checkout_complete.html.erb +23 -9
- data/app/views/spree_google_analytics/_head.html.erb +49 -28
- data/app/views/spree_google_analytics/_product.html.erb +9 -3
- data/app/views/spree_google_analytics/_remove_from_cart.html.erb +10 -4
- data/config/importmap.rb +6 -0
- data/config/initializers/spree.rb +1 -0
- data/config/locales/en.yml +3 -0
- data/lib/spree_google_analytics/engine.rb +12 -1
- data/lib/spree_google_analytics/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 19ff839e89a8bdedb1da8ed93fa302c690211e56ab199beaae23278d51178af8
|
|
4
|
+
data.tar.gz: c55371fada9e8c6d34b82f7bf05ceed492180b492e8b141bee7748846a46c207
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0210987ac97d38ad1e72814a4eb391d2a887fea4af5c15ba429999b0d67ffe08da54d7f04b79a9f67445f7fcf36efa5137331209125e11fa288c30de4e2d35d7'
|
|
7
|
+
data.tar.gz: d777ce1865934d4739ec7c1c1c537b9e5b6987ffa88ba4accd47c5f0372281b8b8e8eb5564639b02d49365526a64cc60c3367633327fd60d1551cf4275e44e17
|
data/README.md
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
|
-
# Spree
|
|
1
|
+
# Google Analytics 4 integration for Spree Commerce
|
|
2
2
|
|
|
3
|
-
This is a Google
|
|
3
|
+
This is a Google Analytics 4 extension for [Spree Commerce](https://spreecommerce.org) - the [open-source eCommerce platform](https://spreecommerce.org) for [Rails](https://spreecommerce.org/category/ruby-on-rails/).
|
|
4
|
+
|
|
5
|
+
This [Google Analytics 4 integration for Spree Commerce](https://spreecommerce.org/docs/integrations/analytics/google-analytics) allows you to track user behavior, sales performance, and marketing effectiveness across your store.
|
|
6
|
+
|
|
7
|
+
With minimal setup required, you can gain valuable insights into how visitors interact with your site, which can help you make informed decisions to improve conversions, user experience, and overall business strategy.
|
|
8
|
+
|
|
9
|
+
> [!NOTE]
|
|
10
|
+
> To set up the Google Analytics integration, you must have a Google Analytics account and an associated property.
|
|
11
|
+
|
|
12
|
+
## Event Tracking
|
|
13
|
+
|
|
14
|
+
By default, the following events will be tracked in Google Analytics 4:
|
|
15
|
+
- page_view
|
|
16
|
+
- first_visit
|
|
17
|
+
- session_start
|
|
18
|
+
- user_engagement
|
|
19
|
+
- search
|
|
20
|
+
- add_payment_info
|
|
21
|
+
- add_shipping_info
|
|
22
|
+
- add_to_cart
|
|
23
|
+
- add_to_wishlist
|
|
24
|
+
- begin_checkout
|
|
25
|
+
- purchase
|
|
26
|
+
- remove_from_cart
|
|
27
|
+
- view_cart
|
|
28
|
+
- view_item
|
|
4
29
|
|
|
5
30
|
## Installation
|
|
6
31
|
|
|
@@ -20,6 +45,10 @@ This is a Google analytics extension for [Spree Commerce](https://spreecommerce.
|
|
|
20
45
|
|
|
21
46
|
If your server was running, restart it so that it can find the assets properly.
|
|
22
47
|
|
|
48
|
+
## Setup
|
|
49
|
+
|
|
50
|
+
Please follow [our setup guide](https://spreecommerce.org/docs/integrations/analytics/google-analytics) to start tracking all events in Google Analytics dashboard.
|
|
51
|
+
|
|
23
52
|
## Developing
|
|
24
53
|
|
|
25
54
|
1. Create a dummy app
|
|
@@ -59,3 +88,15 @@ If you'd like to contribute, please take a look at the
|
|
|
59
88
|
pull request.
|
|
60
89
|
|
|
61
90
|
Copyright (c) 2025 [name of extension creator], released under the New BSD License
|
|
91
|
+
|
|
92
|
+
## Join the Community
|
|
93
|
+
|
|
94
|
+
[Join our Slack](https://slack.spreecommerce.org) to meet other 6k+ community members and get some support.
|
|
95
|
+
|
|
96
|
+
## Need more support?
|
|
97
|
+
|
|
98
|
+
[Contact us](https://spreecommerce.org/contact/) for enterprise support and custom development services. We offer:
|
|
99
|
+
* migrations and upgrades,
|
|
100
|
+
* delivering your Spree application,
|
|
101
|
+
* optimizing your Spree stack.
|
|
102
|
+
|
|
@@ -7,3 +7,14 @@ module SpreeGoogleAnalytics
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
Spree::StoreController.prepend(SpreeGoogleAnalytics::StoreControllerDecorator) if defined?(Spree::StoreController)
|
|
10
|
+
|
|
11
|
+
# include in Devise controllers
|
|
12
|
+
if defined?(Spree::UserSessionsController)
|
|
13
|
+
Spree::UserSessionsController.prepend(SpreeGoogleAnalytics::StoreControllerDecorator)
|
|
14
|
+
end
|
|
15
|
+
if defined?(Spree::UserRegistrationsController)
|
|
16
|
+
Spree::UserRegistrationsController.prepend(SpreeGoogleAnalytics::StoreControllerDecorator)
|
|
17
|
+
end
|
|
18
|
+
if defined?(Spree::UserPasswordsController)
|
|
19
|
+
Spree::UserPasswordsController.prepend(SpreeGoogleAnalytics::StoreControllerDecorator)
|
|
20
|
+
end
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
module SpreeGoogleAnalytics
|
|
2
2
|
module BaseHelper
|
|
3
|
-
def
|
|
4
|
-
|
|
3
|
+
def google_analytics_client_type
|
|
4
|
+
@google_analytics_client_type ||= store_integration('google_analytics')&.preferred_client
|
|
5
|
+
end
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
def use_gtm?
|
|
8
|
+
google_analytics_client_type == 'gtm'
|
|
7
9
|
end
|
|
8
10
|
|
|
9
11
|
def google_analytics_add_shipping_info_json(shipment)
|
|
@@ -21,60 +23,206 @@ module SpreeGoogleAnalytics
|
|
|
21
23
|
}.to_json.html_safe
|
|
22
24
|
end
|
|
23
25
|
|
|
24
|
-
def google_analytics_payment_json
|
|
25
|
-
return unless @order.present?
|
|
26
|
-
|
|
27
|
-
@google_analytics_payment_json ||= SpreeGoogleAnalytics::PaymentPresenter.new(order: @order).call.to_json.html_safe
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def google_analytics_purchase_json
|
|
31
|
-
return unless @order.present?
|
|
32
26
|
|
|
33
|
-
@google_analytics_purchase_json ||= SpreeGoogleAnalytics::OrderPresenter.new(order: @order).call.to_json.html_safe
|
|
34
|
-
end
|
|
35
27
|
|
|
36
28
|
def google_analytics_view_item_json(variant = nil)
|
|
37
29
|
variant ||= @selected_variant || @variant_from_options || @product&.default_variant
|
|
38
30
|
return unless variant.present?
|
|
39
31
|
|
|
32
|
+
if use_gtm?
|
|
33
|
+
google_analytics_gtm_view_item_json(variant)
|
|
34
|
+
else
|
|
35
|
+
{
|
|
36
|
+
currency: current_currency,
|
|
37
|
+
value: variant.amount_in(current_currency).to_f,
|
|
38
|
+
items: [
|
|
39
|
+
SpreeGoogleAnalytics::ProductPresenter.new(
|
|
40
|
+
store_name: current_store.name,
|
|
41
|
+
resource: variant,
|
|
42
|
+
quantity: 1
|
|
43
|
+
).call
|
|
44
|
+
]
|
|
45
|
+
}.to_json.html_safe
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def google_analytics_gtm_view_item_json(variant)
|
|
40
50
|
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
event: 'view_item',
|
|
52
|
+
ecommerce: {
|
|
53
|
+
currency: current_currency,
|
|
54
|
+
value: variant.amount_in(current_currency).to_f,
|
|
55
|
+
items: [
|
|
56
|
+
SpreeGoogleAnalytics::ProductPresenter.new(
|
|
57
|
+
store_name: current_store.name,
|
|
58
|
+
resource: variant,
|
|
59
|
+
quantity: 1
|
|
60
|
+
).call
|
|
61
|
+
]
|
|
62
|
+
}
|
|
50
63
|
}.to_json.html_safe
|
|
51
64
|
end
|
|
52
65
|
|
|
53
66
|
def google_analytics_cart_event_json(line_item, quantity, position)
|
|
67
|
+
if use_gtm?
|
|
68
|
+
google_analytics_gtm_cart_event_json(line_item, quantity, position)
|
|
69
|
+
else
|
|
70
|
+
{
|
|
71
|
+
currency: line_item.currency,
|
|
72
|
+
value: line_item.amount.to_f,
|
|
73
|
+
items: [
|
|
74
|
+
SpreeGoogleAnalytics::ProductPresenter.new(
|
|
75
|
+
resource: line_item,
|
|
76
|
+
quantity: quantity,
|
|
77
|
+
position: position
|
|
78
|
+
).call
|
|
79
|
+
]
|
|
80
|
+
}.to_json.html_safe
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def google_analytics_gtm_cart_event_json(line_item, quantity, position)
|
|
85
|
+
{
|
|
86
|
+
event: 'add_to_cart',
|
|
87
|
+
ecommerce: {
|
|
88
|
+
currency: line_item.currency,
|
|
89
|
+
value: line_item.amount.to_f,
|
|
90
|
+
items: [
|
|
91
|
+
SpreeGoogleAnalytics::ProductPresenter.new(
|
|
92
|
+
resource: line_item,
|
|
93
|
+
quantity: quantity,
|
|
94
|
+
position: position
|
|
95
|
+
).call
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
}.to_json.html_safe
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def google_analytics_remove_from_cart_event_json(line_item, quantity, position)
|
|
102
|
+
if use_gtm?
|
|
103
|
+
google_analytics_gtm_remove_from_cart_event_json(line_item, quantity, position)
|
|
104
|
+
else
|
|
105
|
+
google_analytics_cart_event_json(line_item, quantity, position)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def google_analytics_gtm_remove_from_cart_event_json(line_item, quantity, position)
|
|
54
110
|
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
111
|
+
event: 'remove_from_cart',
|
|
112
|
+
ecommerce: {
|
|
113
|
+
currency: line_item.currency,
|
|
114
|
+
value: line_item.amount.to_f,
|
|
115
|
+
items: [
|
|
116
|
+
SpreeGoogleAnalytics::ProductPresenter.new(
|
|
117
|
+
resource: line_item,
|
|
118
|
+
quantity: quantity,
|
|
119
|
+
position: position
|
|
120
|
+
).call
|
|
121
|
+
]
|
|
122
|
+
}
|
|
64
123
|
}.to_json.html_safe
|
|
65
124
|
end
|
|
66
125
|
|
|
67
126
|
def google_analytics_add_to_wishlist_event_json(variant)
|
|
127
|
+
if use_gtm?
|
|
128
|
+
google_analytics_gtm_add_to_wishlist_event_json(variant)
|
|
129
|
+
else
|
|
130
|
+
{
|
|
131
|
+
currency: current_currency,
|
|
132
|
+
value: variant.amount_in(current_currency),
|
|
133
|
+
items: [
|
|
134
|
+
SpreeGoogleAnalytics::ProductPresenter.new(
|
|
135
|
+
store_name: current_store.name,
|
|
136
|
+
resource: variant,
|
|
137
|
+
quantity: 1
|
|
138
|
+
).call
|
|
139
|
+
]
|
|
140
|
+
}.to_json.html_safe
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def google_analytics_gtm_add_to_wishlist_event_json(variant)
|
|
68
145
|
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
146
|
+
event: 'add_to_wishlist',
|
|
147
|
+
ecommerce: {
|
|
148
|
+
currency: current_currency,
|
|
149
|
+
value: variant.amount_in(current_currency),
|
|
150
|
+
items: [
|
|
151
|
+
SpreeGoogleAnalytics::ProductPresenter.new(
|
|
152
|
+
store_name: current_store.name,
|
|
153
|
+
resource: variant,
|
|
154
|
+
quantity: 1
|
|
155
|
+
).call
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
}.to_json.html_safe
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def google_analytics_checkout_json
|
|
162
|
+
return unless @order.present?
|
|
163
|
+
|
|
164
|
+
if use_gtm?
|
|
165
|
+
google_analytics_gtm_checkout_json
|
|
166
|
+
else
|
|
167
|
+
@google_analytics_checkout_json ||= SpreeGoogleAnalytics::CheckoutPresenter.new(order: @order).call.to_json.html_safe
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def google_analytics_gtm_checkout_json
|
|
172
|
+
@google_analytics_gtm_checkout_json ||= {
|
|
173
|
+
event: 'begin_checkout',
|
|
174
|
+
ecommerce: SpreeGoogleAnalytics::CheckoutPresenter.new(order: @order).call
|
|
175
|
+
}.to_json.html_safe
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def google_analytics_purchase_json
|
|
179
|
+
return unless @order.present?
|
|
180
|
+
|
|
181
|
+
if use_gtm?
|
|
182
|
+
google_analytics_gtm_purchase_json
|
|
183
|
+
else
|
|
184
|
+
@google_analytics_purchase_json ||= SpreeGoogleAnalytics::OrderPresenter.new(order: @order).call.to_json.html_safe
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def google_analytics_gtm_purchase_json
|
|
189
|
+
@google_analytics_gtm_purchase_json ||= {
|
|
190
|
+
event: 'purchase',
|
|
191
|
+
ecommerce: SpreeGoogleAnalytics::OrderPresenter.new(order: @order).call
|
|
192
|
+
}.to_json.html_safe
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def google_analytics_payment_json
|
|
196
|
+
return unless @order.present?
|
|
197
|
+
|
|
198
|
+
if use_gtm?
|
|
199
|
+
google_analytics_gtm_payment_json
|
|
200
|
+
else
|
|
201
|
+
@google_analytics_payment_json ||= SpreeGoogleAnalytics::PaymentPresenter.new(order: @order).call.to_json.html_safe
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def google_analytics_gtm_payment_json
|
|
206
|
+
@google_analytics_gtm_payment_json ||= {
|
|
207
|
+
event: 'add_payment_info',
|
|
208
|
+
ecommerce: SpreeGoogleAnalytics::PaymentPresenter.new(order: @order).call
|
|
209
|
+
}.to_json.html_safe
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def google_analytics_view_cart_json
|
|
213
|
+
return unless @order.present?
|
|
214
|
+
|
|
215
|
+
if use_gtm?
|
|
216
|
+
google_analytics_gtm_view_cart_json
|
|
217
|
+
else
|
|
218
|
+
@google_analytics_view_cart_json ||= SpreeGoogleAnalytics::CheckoutPresenter.new(order: @order).call.to_json.html_safe
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def google_analytics_gtm_view_cart_json
|
|
223
|
+
@google_analytics_gtm_view_cart_json ||= {
|
|
224
|
+
event: 'view_cart',
|
|
225
|
+
ecommerce: SpreeGoogleAnalytics::CheckoutPresenter.new(order: @order).call
|
|
78
226
|
}.to_json.html_safe
|
|
79
227
|
end
|
|
80
228
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import '@hotwired/turbo-rails'
|
|
2
|
+
import { Application } from '@hotwired/stimulus'
|
|
3
|
+
|
|
4
|
+
let application
|
|
5
|
+
|
|
6
|
+
if (typeof window.Stimulus === "undefined") {
|
|
7
|
+
application = Application.start()
|
|
8
|
+
application.debug = false
|
|
9
|
+
window.Stimulus = application
|
|
10
|
+
} else {
|
|
11
|
+
application = window.Stimulus
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
import SpreeGoogleAnalyticsController from 'spree_google_analytics/controllers/spree_google_analytics_controller'
|
|
15
|
+
|
|
16
|
+
application.register('spree_google_analytics', SpreeGoogleAnalyticsController)
|
|
@@ -2,8 +2,12 @@ module Spree
|
|
|
2
2
|
module Integrations
|
|
3
3
|
class GoogleAnalytics < Spree::Integration
|
|
4
4
|
preference :measurement_id, :string
|
|
5
|
+
preference :google_tag_manager_id, :string
|
|
6
|
+
preference :client, :string, default: 'ga4'
|
|
5
7
|
|
|
6
|
-
validates :preferred_measurement_id, presence: true
|
|
8
|
+
validates :preferred_measurement_id, presence: true, if: -> { preferred_client == 'ga4' }
|
|
9
|
+
validates :preferred_google_tag_manager_id, presence: true, if: -> { preferred_client == 'gtm' }
|
|
10
|
+
validates :preferred_client, inclusion: { in: %w[ga4 gtm] }
|
|
7
11
|
|
|
8
12
|
def self.integration_group
|
|
9
13
|
'analytics'
|
|
@@ -1,16 +1,22 @@
|
|
|
1
|
-
<div class="
|
|
2
|
-
<div class="card
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<% end %>
|
|
1
|
+
<div class="card mb-4">
|
|
2
|
+
<div class="card-body" data-controller="reveal">
|
|
3
|
+
<% if @integration.persisted? %>
|
|
4
|
+
<div class="alert alert-warning">
|
|
5
|
+
<%= Spree.t('admin.integrations.google_analytics.reports_warning') %>
|
|
6
|
+
</div>
|
|
7
|
+
<% end %>
|
|
9
8
|
|
|
9
|
+
<%= form.spree_collection_select "preferred_client", [['Google Analytics 4', 'ga4'], ['Google Tag Manager', 'gtm']], :last, :first, { selected: @integration.preferred_client, label: Spree.t('admin.integrations.google_analytics.client'), help: Spree.t('admin.integrations.google_analytics.client_help') }, { data: { action: 'change->reveal#toggle' } } %>
|
|
10
|
+
|
|
11
|
+
<div data-reveal-target="item">
|
|
10
12
|
<%= preference_field(@integration, form, 'measurement_id', i18n_scope: 'admin.integrations.google_analytics') %>
|
|
11
|
-
|
|
12
|
-
<%= external_link_to 'Where to find my Measurement ID?', 'https://support.google.com/analytics/answer/12270356' %>
|
|
13
|
-
</p>
|
|
13
|
+
<%= external_link_to 'Where to find my Measurement ID?', 'https://support.google.com/analytics/answer/12270356' %>
|
|
14
14
|
</div>
|
|
15
|
+
|
|
16
|
+
<div class="hidden" data-reveal-target="item">
|
|
17
|
+
<%= preference_field(@integration, form, 'google_tag_manager_id', i18n_scope: 'admin.integrations.google_analytics') %>
|
|
18
|
+
<%= external_link_to 'Where to find my Google Tag Manager ID?', 'https://support.google.com/tagmanager/answer/6107028' %>
|
|
19
|
+
</div>
|
|
15
20
|
</div>
|
|
16
21
|
</div>
|
|
22
|
+
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
<% if store_integration('google_analytics').present? %>
|
|
2
2
|
<script>
|
|
3
|
-
|
|
4
|
-
<%
|
|
5
|
-
|
|
3
|
+
<% safely do %>
|
|
4
|
+
<% if use_gtm? %>
|
|
5
|
+
if (window.dataLayer) {
|
|
6
|
+
dataLayer.push(<%= google_analytics_cart_event_json(@line_item, @line_item.quantity, @order.line_items.pluck(:id).index(@line_item.id) + 1) %>);
|
|
7
|
+
}
|
|
8
|
+
<% else %>
|
|
9
|
+
if (window.gtag) {
|
|
10
|
+
gtag('event', 'add_to_cart', <%= google_analytics_cart_event_json(@line_item, @line_item.quantity, @order.line_items.pluck(:id).index(@line_item.id) + 1) %>);
|
|
11
|
+
}
|
|
6
12
|
<% end %>
|
|
7
|
-
|
|
13
|
+
<% end %>
|
|
8
14
|
</script>
|
|
9
15
|
<% end %>
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
<% if store_integration('google_analytics').present? && @wished_item&.variant.present? %>
|
|
2
2
|
<script>
|
|
3
|
-
|
|
4
|
-
<%
|
|
5
|
-
|
|
3
|
+
<% safely do %>
|
|
4
|
+
<% if use_gtm? %>
|
|
5
|
+
if (window.dataLayer) {
|
|
6
|
+
dataLayer.push(<%= google_analytics_add_to_wishlist_event_json(@wished_item.variant) %>);
|
|
7
|
+
}
|
|
8
|
+
<% else %>
|
|
9
|
+
if (window.gtag) {
|
|
10
|
+
gtag('event', 'add_to_wishlist', <%= google_analytics_add_to_wishlist_event_json(@wished_item.variant) %>);
|
|
11
|
+
}
|
|
6
12
|
<% end %>
|
|
7
|
-
|
|
13
|
+
<% end %>
|
|
8
14
|
</script>
|
|
9
15
|
<% end %>
|
|
@@ -1,27 +1,61 @@
|
|
|
1
1
|
<% if store_integration('google_analytics').present? %>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
<% if
|
|
6
|
-
<%
|
|
7
|
-
|
|
2
|
+
<% if use_gtm? %>
|
|
3
|
+
<script>
|
|
4
|
+
if (window.dataLayer) {
|
|
5
|
+
<% if controller_name == 'checkout' %>
|
|
6
|
+
<% if @order && session[:checkout_started] %>
|
|
7
|
+
<% safely do %>
|
|
8
|
+
dataLayer.push(<%= google_analytics_checkout_json %>);
|
|
9
|
+
<% end %>
|
|
10
|
+
<% end %>
|
|
11
|
+
|
|
12
|
+
<% if @order && session[:checkout_step_completed] == 'delivery' %>
|
|
13
|
+
<% safely do %>
|
|
14
|
+
<% @order.shipments.each do |shipment| %>
|
|
15
|
+
dataLayer.push({
|
|
16
|
+
event: 'add_shipping_info',
|
|
17
|
+
ecommerce: <%= google_analytics_add_shipping_info_json(shipment) %>
|
|
18
|
+
});
|
|
19
|
+
<% end %>
|
|
20
|
+
<% end %>
|
|
8
21
|
<% end %>
|
|
9
22
|
<% end %>
|
|
10
23
|
|
|
11
|
-
<% if
|
|
24
|
+
<% if controller_name == 'search' && action_name == 'show' && query.present? %>
|
|
12
25
|
<% safely do %>
|
|
13
|
-
|
|
14
|
-
|
|
26
|
+
dataLayer.push({
|
|
27
|
+
event: 'search',
|
|
28
|
+
search_term: "<%= ERB::Util.html_escape(query) %>"
|
|
29
|
+
});
|
|
30
|
+
<% end %>
|
|
31
|
+
<% end %>
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
<% else %>
|
|
35
|
+
<script>
|
|
36
|
+
if (window.gtag) {
|
|
37
|
+
<% if controller_name == 'checkout' %>
|
|
38
|
+
<% if @order && session[:checkout_started] %>
|
|
39
|
+
<% safely do %>
|
|
40
|
+
gtag('event', 'begin_checkout', <%= google_analytics_checkout_json %>);
|
|
41
|
+
<% end %>
|
|
42
|
+
<% end %>
|
|
43
|
+
|
|
44
|
+
<% if @order && session[:checkout_step_completed] == 'delivery' %>
|
|
45
|
+
<% safely do %>
|
|
46
|
+
<% @order.shipments.each do |shipment| %>
|
|
47
|
+
gtag('event', 'add_shipping_info', <%= google_analytics_add_shipping_info_json(shipment) %>);
|
|
48
|
+
<% end %>
|
|
15
49
|
<% end %>
|
|
16
50
|
<% end %>
|
|
17
51
|
<% end %>
|
|
18
|
-
<% end %>
|
|
19
52
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
53
|
+
<% if controller_name == 'search' && action_name == 'show' && query.present? %>
|
|
54
|
+
<% safely do %>
|
|
55
|
+
gtag('event', 'search', <%= raw({ search_term: ERB::Util.html_escape(query) }.to_json) %>);
|
|
56
|
+
<% end %>
|
|
23
57
|
<% end %>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
60
|
+
<% end %>
|
|
27
61
|
<% end %>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<% if store_integration('google_analytics').present? && use_gtm? %>
|
|
2
|
+
<!-- Google Tag Manager (noscript) -->
|
|
3
|
+
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=<%= store_integration('google_analytics').preferred_google_tag_manager_id %>"
|
|
4
|
+
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
|
5
|
+
<!-- End Google Tag Manager (noscript) -->
|
|
6
|
+
<% end %>
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
<% if store_integration('google_analytics').present? %>
|
|
2
2
|
<script>
|
|
3
|
-
if
|
|
4
|
-
<% if
|
|
5
|
-
window.
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
<% if @order.present? && @line_items.any? %>
|
|
4
|
+
<% if use_gtm? %>
|
|
5
|
+
if (window.dataLayer) {
|
|
6
|
+
window.addEventListener('slideover:open', () => {
|
|
7
|
+
dataLayer.push(<%= google_analytics_view_cart_json %>);
|
|
8
|
+
})
|
|
9
|
+
}
|
|
10
|
+
<% else %>
|
|
11
|
+
if (window.gtag) {
|
|
12
|
+
window.addEventListener('slideover:open', () => {
|
|
13
|
+
gtag('event', 'view_cart', <%= google_analytics_view_cart_json %>);
|
|
14
|
+
})
|
|
15
|
+
}
|
|
8
16
|
<% end %>
|
|
9
|
-
|
|
17
|
+
<% end %>
|
|
10
18
|
</script>
|
|
11
19
|
<% end %>
|
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
<% if store_integration('google_analytics').present? %>
|
|
2
2
|
<script>
|
|
3
|
-
|
|
4
|
-
<%
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
<% safely do %>
|
|
4
|
+
<% if use_gtm? %>
|
|
5
|
+
if (window.dataLayer) {
|
|
6
|
+
<% if @order.user.present? %>
|
|
7
|
+
dataLayer.push({
|
|
8
|
+
user_id: "<%= @order.reload.user_id %>",
|
|
9
|
+
user_properties: <%= google_analytics_user_properties_json(@order.user) %>
|
|
10
|
+
});
|
|
11
|
+
<% end %>
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
dataLayer.push(<%= google_analytics_payment_json %>);
|
|
14
|
+
dataLayer.push(<%= google_analytics_purchase_json %>);
|
|
15
|
+
}
|
|
16
|
+
<% else %>
|
|
17
|
+
if (window.gtag) {
|
|
18
|
+
<% if @order.user.present? %>
|
|
19
|
+
gtag('set', 'user_id', "<%= @order.reload.user_id %>");
|
|
20
|
+
gtag('set', 'user_properties', <%= google_analytics_user_properties_json(@order.user) %>);
|
|
21
|
+
<% end %>
|
|
22
|
+
|
|
23
|
+
gtag('event', 'add_payment_info', <%= google_analytics_payment_json %>);
|
|
24
|
+
gtag('event', 'purchase', <%= google_analytics_purchase_json %>);
|
|
25
|
+
}
|
|
12
26
|
<% end %>
|
|
13
|
-
|
|
27
|
+
<% end %>
|
|
14
28
|
</script>
|
|
15
29
|
<% end %>
|
|
@@ -1,34 +1,55 @@
|
|
|
1
1
|
<% if store_integration('google_analytics').present? %>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
<% if use_gtm? %>
|
|
3
|
+
<!-- Google Tag Manager -->
|
|
4
|
+
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
|
5
|
+
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
|
6
|
+
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
|
7
|
+
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
|
8
|
+
})(window,document,'script','dataLayer','<%= store_integration('google_analytics').preferred_google_tag_manager_id %>');</script>
|
|
9
|
+
<!-- End Google Tag Manager -->
|
|
10
|
+
|
|
11
|
+
<script>
|
|
12
|
+
window.dataLayer = window.dataLayer || [];
|
|
13
|
+
|
|
14
|
+
<% if try_spree_current_user %>
|
|
15
|
+
dataLayer.push({
|
|
16
|
+
user_id: "<%= try_spree_current_user.id %>",
|
|
17
|
+
user_properties: <%= google_analytics_user_properties_json(try_spree_current_user) %>
|
|
18
|
+
});
|
|
19
|
+
<% end %>
|
|
20
|
+
</script>
|
|
21
|
+
<% else %>
|
|
22
|
+
<!-- Google tag (gtag.js) -->
|
|
23
|
+
<script async src="https://www.googletagmanager.com/gtag/js?id=<%= store_integration('google_analytics').preferred_measurement_id %>"></script>
|
|
24
|
+
<script>
|
|
25
|
+
window.dataLayer = window.dataLayer || [];
|
|
26
|
+
function gtag(){dataLayer.push(arguments);}
|
|
7
27
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
28
|
+
<% if defined?(store_integration('cookie_first')) && store_integration('cookie_first').present? %>
|
|
29
|
+
gtag('consent', 'default', {
|
|
30
|
+
'ad_storage': 'denied',
|
|
31
|
+
'ad_user_data': 'denied',
|
|
32
|
+
'ad_personalization': 'denied',
|
|
33
|
+
'analytics_storage': 'denied',
|
|
34
|
+
'functionality_storage': 'denied',
|
|
35
|
+
'security_storage': 'granted',
|
|
36
|
+
'wait_for_update': 2000
|
|
37
|
+
});
|
|
38
|
+
<% end %>
|
|
19
39
|
|
|
20
|
-
|
|
21
|
-
|
|
40
|
+
gtag('js', new Date());
|
|
41
|
+
GA_MEASUREMENT_ID = "<%= store_integration('google_analytics').preferred_measurement_id %>";
|
|
22
42
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
<% if ENV['GA_DEBUG_MODE'].present? %>
|
|
44
|
+
gtag('config', GA_MEASUREMENT_ID, { "debug_mode": true });
|
|
45
|
+
<% else %>
|
|
46
|
+
gtag('config', GA_MEASUREMENT_ID);
|
|
47
|
+
<% end %>
|
|
28
48
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
49
|
+
<% if try_spree_current_user %>
|
|
50
|
+
gtag('set', 'user_id', "<%= try_spree_current_user.id %>");
|
|
51
|
+
gtag('set', 'user_properties', <%= google_analytics_user_properties_json(try_spree_current_user) %>);
|
|
52
|
+
<% end %>
|
|
53
|
+
</script>
|
|
54
|
+
<% end %>
|
|
34
55
|
<% end %>
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
<% if store_integration('google_analytics').present? %>
|
|
2
2
|
<script>
|
|
3
|
-
if
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
<% if use_gtm? %>
|
|
4
|
+
if (window.dataLayer) {
|
|
5
|
+
dataLayer.push(<%= google_analytics_view_item_json %>);
|
|
6
|
+
}
|
|
7
|
+
<% else %>
|
|
8
|
+
if (window.gtag) {
|
|
9
|
+
gtag('event', 'view_item', <%= google_analytics_view_item_json %>);
|
|
10
|
+
}
|
|
11
|
+
<% end %>
|
|
6
12
|
</script>
|
|
7
13
|
<% end %>
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
<% if store_integration('google_analytics').present? %>
|
|
2
2
|
<script>
|
|
3
|
-
|
|
4
|
-
<%
|
|
5
|
-
|
|
3
|
+
<% safely do %>
|
|
4
|
+
<% if use_gtm? %>
|
|
5
|
+
if (window.dataLayer) {
|
|
6
|
+
dataLayer.push(<%= google_analytics_remove_from_cart_event_json(@line_item, @line_item.quantity, nil) %>);
|
|
7
|
+
}
|
|
8
|
+
<% else %>
|
|
9
|
+
if (window.gtag) {
|
|
10
|
+
gtag('event', 'remove_from_cart', <%= google_analytics_remove_from_cart_event_json(@line_item, @line_item.quantity, nil) %>);
|
|
11
|
+
}
|
|
6
12
|
<% end %>
|
|
7
|
-
|
|
13
|
+
<% end %>
|
|
8
14
|
</script>
|
|
9
15
|
<% end %>
|
data/config/importmap.rb
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
pin 'application-spree_google_analytics', to: 'spree_google_analytics/application.js', preload: false
|
|
2
|
+
|
|
3
|
+
pin_all_from SpreeGoogleAnalytics::Engine.root.join('app/javascript/spree_google_analytics/controllers'),
|
|
4
|
+
under: 'spree_google_analytics/controllers',
|
|
5
|
+
to: 'spree_google_analytics/controllers',
|
|
6
|
+
preload: 'application-spree_google_analytics'
|
|
@@ -3,6 +3,7 @@ Rails.application.config.after_initialize do
|
|
|
3
3
|
|
|
4
4
|
if Rails.application.config.respond_to?(:spree_storefront)
|
|
5
5
|
Rails.application.config.spree_storefront.head_partials << 'spree_google_analytics/head'
|
|
6
|
+
Rails.application.config.spree_storefront.body_start_partials << 'spree_google_analytics/body_start'
|
|
6
7
|
Rails.application.config.spree_storefront.body_end_partials << 'spree_google_analytics/body_end'
|
|
7
8
|
|
|
8
9
|
Rails.application.config.spree_storefront.cart_partials << 'spree_google_analytics/cart'
|
data/config/locales/en.yml
CHANGED
|
@@ -8,3 +8,6 @@ en:
|
|
|
8
8
|
reports_warning: It can take up to 48 hours for the data to appear in standard reports
|
|
9
9
|
measurement_id: Measurement ID
|
|
10
10
|
api_secret: API Secret
|
|
11
|
+
google_tag_manager_id: Google Tag Manager ID
|
|
12
|
+
client: Client Type
|
|
13
|
+
client_help: Choose between Google Analytics 4 (direct gtag.js implementation) or Google Tag Manager (dataLayer events)
|
|
@@ -14,7 +14,18 @@ module SpreeGoogleAnalytics
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
initializer 'spree_google_analytics.assets' do |app|
|
|
17
|
-
app.config.assets
|
|
17
|
+
if app.config.respond_to?(:assets)
|
|
18
|
+
app.config.assets.paths << root.join('app/javascript')
|
|
19
|
+
app.config.assets.paths << root.join('vendor/javascript')
|
|
20
|
+
app.config.assets.paths << root.join('vendor/stylesheets')
|
|
21
|
+
app.config.assets.precompile += %w[spree_google_analytics_manifest]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
initializer 'spree_google_analytics.importmap', before: 'importmap' do |app|
|
|
26
|
+
app.config.importmap.paths << root.join('config/importmap.rb')
|
|
27
|
+
# https://github.com/rails/importmap-rails?tab=readme-ov-file#sweeping-the-cache-in-development-and-test
|
|
28
|
+
app.config.importmap.cache_sweepers << root.join('app/javascript')
|
|
18
29
|
end
|
|
19
30
|
|
|
20
31
|
def self.activate
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_google_analytics
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vendo Connect Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-11-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: spree
|
|
@@ -79,6 +79,8 @@ files:
|
|
|
79
79
|
- app/assets/images/integration_icons/google-analytics-logo.png
|
|
80
80
|
- app/controllers/spree_google_analytics/store_controller_decorator.rb
|
|
81
81
|
- app/helpers/spree_google_analytics/base_helper.rb
|
|
82
|
+
- app/javascript/spree_google_analytics/application.js
|
|
83
|
+
- app/javascript/spree_google_analytics/controllers/spree_google_analytics_controller.js
|
|
82
84
|
- app/models/spree/integrations/google_analytics.rb
|
|
83
85
|
- app/presenters/spree_google_analytics/checkout_presenter.rb
|
|
84
86
|
- app/presenters/spree_google_analytics/order_presenter.rb
|
|
@@ -88,11 +90,13 @@ files:
|
|
|
88
90
|
- app/views/spree_google_analytics/_add_to_cart.html.erb
|
|
89
91
|
- app/views/spree_google_analytics/_add_to_wishlist.erb
|
|
90
92
|
- app/views/spree_google_analytics/_body_end.html.erb
|
|
93
|
+
- app/views/spree_google_analytics/_body_start.html.erb
|
|
91
94
|
- app/views/spree_google_analytics/_cart.html.erb
|
|
92
95
|
- app/views/spree_google_analytics/_checkout_complete.html.erb
|
|
93
96
|
- app/views/spree_google_analytics/_head.html.erb
|
|
94
97
|
- app/views/spree_google_analytics/_product.html.erb
|
|
95
98
|
- app/views/spree_google_analytics/_remove_from_cart.html.erb
|
|
99
|
+
- config/importmap.rb
|
|
96
100
|
- config/initializers/spree.rb
|
|
97
101
|
- config/locales/en.yml
|
|
98
102
|
- config/routes.rb
|