spree_analytics_trackers 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +21 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +29 -0
  5. data/Appraisals +17 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE +26 -0
  8. data/README.md +73 -0
  9. data/Rakefile +21 -0
  10. data/app/controllers/spree/admin/trackers_controller.rb +6 -0
  11. data/app/controllers/spree/store_controller_decorator.rb +5 -0
  12. data/app/helpers/spree/trackers_helper.rb +23 -0
  13. data/app/models/spree/line_item_decorator.rb +6 -0
  14. data/app/models/spree/order_decorator.rb +7 -0
  15. data/app/models/spree/product_decorator.rb +11 -0
  16. data/app/models/spree/tracker.rb +25 -0
  17. data/app/models/spree/user_decorator.rb +5 -0
  18. data/app/overrides/add_analytics_to_admin_sidebar.rb +9 -0
  19. data/app/overrides/add_cart_viewed_to_orders_edit.rb +10 -0
  20. data/app/overrides/add_checkout_step_viewed_to_checkout_edit.rb +10 -0
  21. data/app/overrides/add_google_analytics_to_spree_application.rb +9 -0
  22. data/app/overrides/add_google_checkout_to_checkout_edit.rb +9 -0
  23. data/app/overrides/add_google_purchase_to_orders_show.rb +9 -0
  24. data/app/overrides/add_order_complete_to_orders_show.rb +10 -0
  25. data/app/overrides/add_product_added_to_orders_edit.rb +10 -0
  26. data/app/overrides/add_product_list_filtered_to_taxons_show.rb +10 -0
  27. data/app/overrides/add_product_list_viewed_to_products_list.rb +10 -0
  28. data/app/overrides/add_product_viewed_to_products_show.rb +10 -0
  29. data/app/overrides/add_products_searched_to_products_list.rb +10 -0
  30. data/app/overrides/add_segment_initializer_to_layout.rb +10 -0
  31. data/app/views/spree/admin/shared/_analytics_sidebar_menu.html.erb +5 -0
  32. data/app/views/spree/admin/trackers/_form.html.erb +36 -0
  33. data/app/views/spree/admin/trackers/edit.html.erb +13 -0
  34. data/app/views/spree/admin/trackers/index.html.erb +44 -0
  35. data/app/views/spree/admin/trackers/new.html.erb +13 -0
  36. data/app/views/spree/shared/_google_add_items.html.erb +13 -0
  37. data/app/views/spree/shared/_google_analytics.html.erb +18 -0
  38. data/app/views/spree/shared/_google_checkout.html.erb +14 -0
  39. data/app/views/spree/shared/_google_purchase.html.erb +19 -0
  40. data/app/views/spree/shared/trackers/segment/_cart_viewed.js.erb +22 -0
  41. data/app/views/spree/shared/trackers/segment/_checkout_step_viewed.js.erb +12 -0
  42. data/app/views/spree/shared/trackers/segment/_initializer.js.erb +8 -0
  43. data/app/views/spree/shared/trackers/segment/_order_complete.js.erb +48 -0
  44. data/app/views/spree/shared/trackers/segment/_product_added.js.erb +9 -0
  45. data/app/views/spree/shared/trackers/segment/_product_list_filtered.js.erb +32 -0
  46. data/app/views/spree/shared/trackers/segment/_product_list_viewed.js.erb +22 -0
  47. data/app/views/spree/shared/trackers/segment/_product_viewed.js.erb +6 -0
  48. data/app/views/spree/shared/trackers/segment/_products_searched.js.erb +12 -0
  49. data/bin/rails +7 -0
  50. data/config/locales/en.yml +20 -0
  51. data/config/routes.rb +5 -0
  52. data/db/migrate/20171013160337_create_spree_trackers.rb +20 -0
  53. data/gemfiles/.bundle/config +2 -0
  54. data/gemfiles/spree_3_5.gemfile +9 -0
  55. data/gemfiles/spree_3_7.gemfile +9 -0
  56. data/gemfiles/spree_master.gemfile +10 -0
  57. data/lib/generators/spree_analytics_trackers/install/install_generator.rb +20 -0
  58. data/lib/spree_analytics_trackers.rb +4 -0
  59. data/lib/spree_analytics_trackers/engine.rb +20 -0
  60. data/lib/spree_analytics_trackers/factories.rb +9 -0
  61. data/lib/spree_analytics_trackers/version.rb +17 -0
  62. data/spec/features/admin/configuration/analytics_tracker_spec.rb +50 -0
  63. data/spec/models/spree/tracker_spec.rb +17 -0
  64. data/spec/spec_helper.rb +93 -0
  65. data/spree_analytics_trackers.gemspec +44 -0
  66. metadata +75 -8
@@ -0,0 +1,10 @@
1
+ spree_version = Gem.loaded_specs['spree_core'].version
2
+ unless spree_version >= Gem::Version.create('3.4.0') && spree_version < Gem::Version.create('3.5.0')
3
+ Deface::Override.new(
4
+ virtual_path: 'spree/products/show',
5
+ name: 'add_product_viewed_to_products_show',
6
+ insert_before: "[data-hook='product_show']",
7
+ partial: 'spree/shared/trackers/segment/product_viewed.js',
8
+ original: '5426c263ccfe477b270b55024abebc77cf951c91'
9
+ )
10
+ end
@@ -0,0 +1,10 @@
1
+ spree_version = Gem.loaded_specs['spree_core'].version
2
+ unless spree_version >= Gem::Version.create('3.4.0') && spree_version < Gem::Version.create('3.5.0')
3
+ Deface::Override.new(
4
+ virtual_path: 'spree/shared/_products',
5
+ name: 'add_products_searched_to_products_list',
6
+ insert_before: "[data-hook='products_search_results_heading']",
7
+ partial: 'spree/shared/trackers/segment/products_searched.js',
8
+ original: '8b47dddca9dfaaacca3d462459c6c1ef06c09926'
9
+ )
10
+ end
@@ -0,0 +1,10 @@
1
+ spree_version = Gem.loaded_specs['spree_core'].version
2
+ unless spree_version >= Gem::Version.create('3.3.0') && spree_version < Gem::Version.create('3.5.0') && spree_version != Gem::Version.create('3.5.0.alpha')
3
+ Deface::Override.new(
4
+ virtual_path: 'spree/layouts/spree_application',
5
+ name: 'add_segment_initializer_to_layout',
6
+ insert_top: "[data-hook='body']",
7
+ partial: 'spree/shared/trackers/segment/initializer.js',
8
+ original: '26b91c1073b75c93c9e9c89b1f0a6f52ef29a8f9'
9
+ )
10
+ end
@@ -0,0 +1,5 @@
1
+ <% if can? :admin, Spree::Tracker %>
2
+ <ul class="nav nav-sidebar">
3
+ <%= tab :analytic_trackers, url: spree.admin_trackers_path, icon: 'tasks' %>
4
+ </ul>
5
+ <% end %>
@@ -0,0 +1,36 @@
1
+ <div data-hook="admin_tracker_form_fields" class="row">
2
+ <div class="col-xs-12 col-md-4" data-hook="analytics_engine">
3
+ <%= f.field_container :engine, class: ["form-group"], "data-hook" => "analytics_engine" do %>
4
+ <%= f.label :engine, Spree.t(:analytics_engine) %>
5
+ <%= f.select :engine, Spree::Tracker.engines.keys.sort.map { |k| [k.humanize, k] }, {}, { class: 'select2' } %>
6
+ <%= f.error_message_on :engine %>
7
+ <% end %>
8
+ </div>
9
+ <div class="col-xs-12 col-md-8">
10
+ <div data-hook="active" class="form-group">
11
+ <%= label_tag nil, Spree.t(:active) %>
12
+ <div class="radio">
13
+ <%= label_tag :tracker_active_true do %>
14
+ <%= radio_button(:tracker, :active, true) %>
15
+ <%= Spree.t(:say_yes) %>
16
+ <% end %>
17
+ </div>
18
+ <div class="radio">
19
+ <%= label_tag :tracker_active_false do %>
20
+ <%= radio_button(:tracker, :active, false) %>
21
+ <%= Spree.t(:say_no) %>
22
+ <% end %>
23
+ </div>
24
+ </div>
25
+ </div>
26
+ <div class="col-xs-12 col-md-4" data-hook="analytics_id">
27
+ <%= f.field_container :analytics_id, class: ["form-group"], "data-hook" => "analytics_id" do %>
28
+ <%= f.label :analytics_id, Spree.t(:google_analytics_id) %>
29
+ <%= f.text_field :analytics_id, class: 'form-control' %>
30
+ <%= f.error_message_on :analytics_id %>
31
+ <% end %>
32
+ </div>
33
+ <div data-hook="additional_tracker_fields"></div>
34
+ </div>
35
+ <div data-hook="admin_tracker_form_fields" class="row">
36
+ </div>
@@ -0,0 +1,13 @@
1
+ <% content_for :page_title do %>
2
+ <%= link_to Spree.t(:analytics_trackers), spree.admin_trackers_url %> /
3
+ <%= @tracker.analytics_id %>
4
+ <% end %>
5
+
6
+ <%= render partial: 'spree/admin/shared/error_messages', locals: { target: @tracker } %>
7
+
8
+ <%= form_for [:admin, @tracker] do |f| %>
9
+ <fieldset>
10
+ <%= render partial: 'form', locals: { f: f } %>
11
+ <%= render partial: 'spree/admin/shared/edit_resource_links' %>
12
+ </fieldset>
13
+ <% end %>
@@ -0,0 +1,44 @@
1
+ <% content_for :page_title do %>
2
+ <%= Spree.t(:analytics_trackers) %>
3
+ <% end %>
4
+
5
+ <% content_for :page_actions do %>
6
+ <%= button_link_to Spree.t(:new_tracker), new_object_url, class: "btn-success", icon: 'add', id: 'admin_new_tracker_link' %>
7
+ <% end if can? :create, Spree::Tracker %>
8
+
9
+ <% if @trackers.any? %>
10
+ <table class="table">
11
+ <colgroup>
12
+ <col style="width: 30%">
13
+ <col style="width: 40%">
14
+ <col style="width: 15%">
15
+ <col style="width: 15%">
16
+ </colgroup>
17
+ <thead>
18
+ <tr data-hook="admin_trackers_index_headers">
19
+ <th><%= Spree.t(:google_analytics_id) %></th>
20
+ <th><%= Spree.t(:analytics_engine) %></th>
21
+ <th class="text-center"><%= Spree.t(:active) %></th>
22
+ <th class="actions"></th>
23
+ </tr>
24
+ </thead>
25
+ <tbody>
26
+ <% @trackers.each do |tracker|%>
27
+ <tr id="<%= spree_dom_id tracker %>" data-hook="admin_trackers_index_rows">
28
+ <td><%= tracker.analytics_id %></td>
29
+ <td><%= tracker.engine.humanize %></td>
30
+ <td class="text-center"><%= tracker.active ? Spree.t(:say_yes) : Spree.t(:say_no) %></td>
31
+ <td class="actions text-right">
32
+ <%= link_to_edit(tracker, no_text: true) if can? :edit, tracker %>
33
+ <%= link_to_delete(tracker, no_text: true) if can? :delete, tracker %>
34
+ </td>
35
+ </tr>
36
+ <% end %>
37
+ </tbody>
38
+ </table>
39
+ <% else %>
40
+ <div class="alert alert-warning">
41
+ <%= Spree.t(:no_resource_found, resource: plural_resource_name(Spree::Tracker)) %>,
42
+ <%= link_to(Spree.t(:add_one), new_object_url) if can? :create, Spree::Tracker %>!
43
+ </div>
44
+ <% end %>
@@ -0,0 +1,13 @@
1
+ <% content_for :page_title do %>
2
+ <%= link_to Spree.t(:analytics_trackers), spree.admin_trackers_url %> /
3
+ <%= Spree.t(:new_tracker) %>
4
+ <% end %>
5
+
6
+ <%= render partial: 'spree/admin/shared/error_messages', locals: { target: @tracker } %>
7
+
8
+ <%= form_for [:admin, @tracker] do |f| %>
9
+ <fieldset>
10
+ <%= render partial: 'form', locals: { f: f } %>
11
+ <%= render partial: 'spree/admin/shared/new_resource_links' %>
12
+ </fieldset>
13
+ <% end %>
@@ -0,0 +1,13 @@
1
+ if (typeof ga !== 'undefined') {
2
+ <% @order.line_items.each do |line_item| %>
3
+ ga('ec:addProduct', { // Provide product details in a productFieldObject.
4
+ 'id': '<%= j (line_item.sku.present? ? line_item.sku : line_item.variant_id.to_s) %>', // Product ID (string).
5
+ 'name': '<%= j line_item.name %>', // Product name (string).
6
+ 'category': '<%= j line_item.category.try(:name) %>', // Product category (string).
7
+ 'brand': '<%= j line_item.brand.try(:name) %>', // Product brand (string).
8
+ 'variant': '<%= j line_item.options_text %>', // Product variant (string).
9
+ 'price': '<%= line_item.price %>',
10
+ 'quantity': <%= line_item.quantity %>
11
+ });
12
+ <% end %>
13
+ }
@@ -0,0 +1,18 @@
1
+ <% if tracker = Spree::Tracker.current(:google_analytics) %>
2
+ <script>
3
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
4
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
5
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
6
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
7
+
8
+ <% if user_analytics_id = try_spree_current_user.try(:analytics_id) %>
9
+ ga('create', { 'trackingId': '<%= tracker.analytics_id %>', 'cookieDomain': 'auto', 'userId': '<%= user_analytics_id %>' });
10
+ <% else %>
11
+ ga('create', '<%= tracker.analytics_id %>', 'auto');
12
+ <% end %>
13
+
14
+ ga('require', 'displayfeatures');
15
+ <%= yield :google_analytics %>
16
+ ga('send', 'pageview');
17
+ </script>
18
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <%# more info: https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce %>
2
+ <script>
3
+ if (typeof ga !== 'undefined') {
4
+ ga('require', 'ec');
5
+ ga('set', '&cu', '<%= current_currency %>');
6
+
7
+ <%= render partial: 'spree/shared/google_add_items', locals: { order: @order } %>
8
+
9
+ // Add the step number and additional info about the checkout to the action.
10
+ ga('ec:setAction','checkout', {
11
+ 'step': <%= (@order.checkout_steps.index(@order.state) + 1) %>
12
+ });
13
+ }
14
+ </script>
@@ -0,0 +1,19 @@
1
+ <% if order_just_completed?(@order) %>
2
+ <script>
3
+ <%# more info: https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce %>
4
+ if (typeof ga !== 'undefined') {
5
+ ga('require', 'ec');
6
+ ga('set', '&cu', '<%= current_currency %>');
7
+
8
+ <%= render partial: 'spree/shared/google_add_items', locals: { order: @order } %>
9
+
10
+ ga('ec:setAction', 'purchase', { // Transaction details are provided in an actionFieldObject.
11
+ 'id': '<%= j @order.number %>', // Transaction ID. Required.
12
+ 'affiliation': '<%= current_store.name %>', // Affiliation (string).
13
+ 'revenue': '<%= @order.total %>', // Revenue (currency).
14
+ 'tax': '<%= @order.tax_total %>', // Tax.
15
+ 'shipping': '<%= @order.ship_total %>' // Shipping.
16
+ });
17
+ }
18
+ </script>
19
+ <% end %>
@@ -0,0 +1,22 @@
1
+ <script>
2
+ if (typeof analytics !== 'undefined') {
3
+ analytics.track('Cart Viewed', {
4
+ cart_id: '<%= @order.number %>',
5
+ products: [
6
+ <% @order.line_items.each_with_index do |line_item, index| %>
7
+ <%= product_for_segment(line_item, position: index+1, quantity: line_item.quantity) %>,
8
+ <% end %>
9
+ ]
10
+ });
11
+
12
+ analytics.page('Cart Viewed', {
13
+ cart_id: '<%= @order.number %>',
14
+ products: [
15
+ <% @order.line_items.each_with_index do |line_item, index| %>
16
+ <%= product_for_segment(line_item, position: index+1, quantity: line_item.quantity) %>,
17
+ <% end %>
18
+ ]
19
+ });
20
+
21
+ }
22
+ </script>
@@ -0,0 +1,12 @@
1
+ <script>
2
+ if (typeof analytics !== 'undefined') {
3
+ analytics.track('Checkout Step Viewed', {
4
+ checkout_id: '<%= @order.number %>',
5
+ step: '<%= (@order.checkout_steps.index(@order.state) + 1) %>'
6
+ });
7
+ analytics.page('Checkout Step Viewed', {
8
+ checkout_id: '<%= @order.number %>',
9
+ step: '<%= (@order.checkout_steps.index(@order.state) + 1) %>'
10
+ });
11
+ }
12
+ </script>
@@ -0,0 +1,8 @@
1
+ <% if tracker = Spree::Tracker.current(:segment) %>
2
+ <script>
3
+ !function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)}analytics.load=function(t,e){var n=document.createElement("script");n.type="text/javascript";n.async=!0;n.src="https://cdn.segment.com/analytics.js/v1/"+t+"/analytics.min.js";var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(n,a);analytics._loadOptions=e};analytics.SNIPPET_VERSION="4.1.0";
4
+ analytics.load('<%=tracker.analytics_id%>'.replace(/^\s+|\s+$/g, ''));
5
+ analytics.page()
6
+ }}();
7
+ </script>
8
+ <% end %>
@@ -0,0 +1,48 @@
1
+ <% if order_just_completed?(@order) %>
2
+ <script>
3
+ if (typeof analytics !== 'undefined') {
4
+ analytics.track('Order Completed', {
5
+ order_id: '<%= @order.number %>',
6
+ total: <%= @order.total %>,
7
+ shipping: <%= @order.shipment_total %>,
8
+ tax: <%= @order.additional_tax_total %>,
9
+ discount: <%= @order.promo_total %>,
10
+ coupon: '<%= @order.promo_code %>',
11
+ currency: '<%= @order.currency %>',
12
+ products: [
13
+ <% @order.line_items.each do |line_item| %>
14
+ {
15
+ product_id: '<%= line_item.product.id %>',
16
+ sku: '<%= line_item.variant.sku %>',
17
+ name: '<%= line_item.product.name %>',
18
+ price: <%= line_item.price %>,
19
+ quantity: <%= line_item.quantity %>
20
+ },
21
+ <% end %>
22
+ ]
23
+ });
24
+
25
+ analytics.page('Order Completed', {
26
+ order_id: '<%= @order.number %>',
27
+ total: <%= @order.total %>,
28
+ shipping: <%= @order.shipment_total %>,
29
+ tax: <%= @order.additional_tax_total %>,
30
+ discount: <%= @order.promo_total %>,
31
+ coupon: '<%= @order.promo_code %>',
32
+ currency: '<%= @order.currency %>',
33
+ products: [
34
+ <% @order.line_items.each do |line_item| %>
35
+ {
36
+ product_id: '<%= line_item.product.id %>',
37
+ sku: '<%= line_item.variant.sku %>',
38
+ name: '<%= line_item.product.name %>',
39
+ price: <%= line_item.price %>,
40
+ quantity: <%= line_item.quantity %>
41
+ },
42
+ <% end %>
43
+ ]
44
+ });
45
+
46
+ }
47
+ </script>
48
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <% if @variant %>
2
+ <script>
3
+ if (typeof analytics !== 'undefined') {
4
+ analytics.track('Product Added', <%= product_for_segment(@variant.product, cart_id: @order.number) %>);
5
+ analytics.page('Product Added', <%= product_for_segment(@variant.product, cart_id: @order.number) %>);
6
+
7
+ }
8
+ </script>
9
+ <% end %>
@@ -0,0 +1,32 @@
1
+ <% if params[:search].present? %>
2
+ <script>
3
+ if (typeof analytics !== 'undefined') {
4
+ analytics.track('Product List Filtered', {
5
+ filters: [
6
+ <% params[:search].to_unsafe_h.each do |type, value| %>
7
+ <%= { type: type, value: value } .to_json.html_safe %>,
8
+ <% end %>
9
+ ],
10
+ products: [
11
+ <% @products.each_with_index do |product, index| %>
12
+ <%= product_for_segment(product, position: index+1, image: product.images.first) %>,
13
+ <% end %>
14
+ ]
15
+ });
16
+
17
+ analytics.page('Product List Filtered', {
18
+ filters: [
19
+ <% params[:search].to_unsafe_h.each do |type, value| %>
20
+ <%= { type: type, value: value } .to_json.html_safe %>,
21
+ <% end %>
22
+ ],
23
+ products: [
24
+ <% @products.each_with_index do |product, index| %>
25
+ <%= product_for_segment(product, position: index+1, image: product.images.first) %>,
26
+ <% end %>
27
+ ]
28
+ });
29
+
30
+ }
31
+ </script>
32
+ <% end %>
@@ -0,0 +1,22 @@
1
+ <script>
2
+ if (typeof analytics !== 'undefined') {
3
+ analytics.track('Product List Viewed', {
4
+ category: '<%= @taxon.try(:name) %>',
5
+ products: [
6
+ <% products.each_with_index do |product, index| %>
7
+ <%= product_for_segment(product, position: index+1, image: product.images.first) %>,
8
+ <% end %>
9
+ ]
10
+ });
11
+
12
+ analytics.page('Product List Viewed', {
13
+ category: '<%= @taxon.try(:name) %>',
14
+ products: [
15
+ <% products.each_with_index do |product, index| %>
16
+ <%= product_for_segment(product, position: index+1, image: product.images.first) %>,
17
+ <% end %>
18
+ ]
19
+ });
20
+
21
+ }
22
+ </script>
@@ -0,0 +1,6 @@
1
+ <script>
2
+ if (typeof analytics !== 'undefined') {
3
+ analytics.track('Product Viewed', <%= product_for_segment(@product, image: @product.images.first) %>);
4
+ analytics.page('Product Viewed', <%= product_for_segment(@product, image: @product.images.first) %>);
5
+ }
6
+ </script>
@@ -0,0 +1,12 @@
1
+ <% if params.key?(:keywords) %>
2
+ <script>
3
+ if (typeof analytics !== 'undefined') {
4
+ analytics.track('Products Searched', {
5
+ query: '<%= params[:keywords] %>',
6
+ });
7
+ analytics.page('Products Searched', {
8
+ query: '<%= params[:keywords] %>',
9
+ });
10
+ }
11
+ </script>
12
+ <% end %>
data/bin/rails ADDED
@@ -0,0 +1,7 @@
1
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
2
+
3
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
4
+ ENGINE_PATH = File.expand_path('../../lib/spree_analytics_trackers/engine', __FILE__)
5
+
6
+ require 'rails/all'
7
+ require 'rails/engine/commands'
@@ -0,0 +1,20 @@
1
+ en:
2
+ activerecord:
3
+ attributes:
4
+ spree/tracker:
5
+ one: Tracker
6
+ other: Trackers
7
+ spree:
8
+ analytics_engine: Analytics Engine
9
+ analytics_trackers: Analytics Trackers
10
+ new_tracker: New Tracker
11
+ google_analytics: Google Analytics
12
+ google_analytics_id: Analytics ID
13
+ analytics_desc_header_1: Spree Analytics
14
+ analytics_desc_header_2: Live analytics integrated into your Spree dashboard
15
+ analytics_desc_list_1: Get live sales information as it happens
16
+ analytics_desc_list_2: Requires only a free Spree account to activate
17
+ analytics_desc_list_3: Absolutely no code to install
18
+ analytics_desc_list_4: It's completely free!
19
+ analytics_engine: Analytics Engine
20
+ analytics_trackers: Analytics Trackers