[Feature] Analytics & Reporting - restarone/violet_rails GitHub Wiki

Opting into Analytics

Analytics & tracking are opt-in features due to privacy concerns. We as a vendor do not enable this feature out of the box.

Enable tracking

To enable tracking, visit Violet Rails Admin - Web Settings and mark the tracking checkbox: Screenshot from 2022-06-01 17-18-23

Select a data retention policy

Before opting into analytics its advisable to choose a data retention policy (for GDPR compliance for example) by navigating to Violet Rails Admin - Web Settings and selecting how often you want the system to purge analytics related data: Screenshot from 2022-06-01 17-15-54

Analytics

Violet Rails bundles the definitive & battle-tested ™️ first-party tracking gem for Rails: https://github.com/ankane/ahoy and integrates its tracking capabilities with your business application seamlessly. In addition to tracking, Violet also provides various dynamically generated dashboards for gleaning insights into your website analytics and business logic.

trackable events

  1. Section Views - when visitors read parts of the page
  2. Video watch time - find out how long someone watched a video before they bounced
  3. Clicks - find out which parts of your website and app attract attention
  4. Form submissions - find out how often people submit information and payments
  5. Any other event - see developer docs for custom events

Tracking Clicks

To track clicks/touches on any DOM element. Append the following data attributes

  • data-violet-track-click="true" -> this tells Violet to track clicks
  • data-violet-event-name="your-dasherized-unique-identifier-here" -> this tells Violet which event to track clicks under
  • data-violet-event-label="your human friendly label here" -> this sets the label of the event

Visual analytics

First time and recurring visit tracking and visualization

  • see visit tracking segmented by time period and page
  • see trends compared to the previous period
  • see complete event list per visit and visitor
Screen Shot 2023-03-16 at 12 28 56 PM

Video watch time

  • track video watch time reliably, even when the visitor quits the browser
  • see analytics on a per video basis
  • see demographics on a per video basis
Screen Shot 2023-03-16 at 12 29 08 PM

Section Views and Form Submissions

  • track which sections are being viewed and how often
  • track form submissions over periods of time
Screen Shot 2023-03-16 at 12 30 01 PM

Numerical analytics

It is able to visualize and query analytics data:

Screenshot from 2022-05-28 10-06-23

Custom Analytics

and show analytics events grouped by name: Screenshot from 2022-05-28 10-06-40

To track custom events in the CMS, simply call ahoy in a script tag or in your javascript layout:

full docs: https://github.com/ankane/ahoy.js

ahoy.track("My second event", {language: "JavaScript"});

and it will show up as a custom event image

see demo video here: https://user-images.githubusercontent.com/25191509/169423339-76a21a86-eed8-4999-bce8-28fa48da326f.mp4

All analytics can be queried via GraphQL. See here for usage: https://github.com/restarone/violet_rails/blob/master/test/controllers/graphql_controller_test.rb

#Reporting A periodic report delivered to you containing stats from your app.

Setup

Recipients

Select the users you want to have access to this feature and make sure Deliver Analytics Report is checked. user-config

Report Frequency

Next select how often you want to see a report by navigating to Admin -> Web Settings. This will send the report periodically to the users you have selected subdomain-config

Visitor analytics on Nikean website

You can define and track custom events such as when someone visits a page or clicks a button with ahoy.js.

Call ahoy.track to track an event. To register a single occurrence of an event, you can save an object containing information related to analytics and visitor events to session storage.

To detect when someone visits a section on a page, you can use the Intersection Observer API.

Example

<main>
  <section>
    <h1>Tell Your Psychedelic Story</h1>
    <button data-analytics-id="hero-section-story-button">CTA</button>
  </section>
  <section data-analytics-id="share-story">
    <h2>Share Your Psychedelic Story</h2>
    <button data-analytics-id="share-story-section-story-button">CTA</button>
  </section>
  <section data-analytics-id="join-story-project">
    <h2>Join The Psychedelic Storytelling Project</h2>
    <button data-analytics-id="join-story-project-section-story-button">CTA</button>
  </section>
  <section data-analytics-id="quotes">
    <!-- content here -->
  </section>
</main>
<script>
  function initAnalytics() {
    const analyticsBrowserStorageKey = "nikeanLandingAnalytics";
		const sectionsViewedMap = {
      "share-story": false,
      "join-story-project": false,
      quotes: false,
      partners: false,
    }
    const landingAnalytics = JSON.parse(
      sessionStorage.getItem(analyticsBrowserStorageKey)
    );
    if (!landingAnalytics) {
      const value = {
        analyticsHaveBeenAdded: false,
        sectionsViewedMap
      };
      sessionStorage.setItem(analyticsBrowserStorageKey, JSON.stringify(value));
    }

    const analyticsHaveBeenAdded = landingAnalytics?.["analyticsHaveBeenAdded"];
    if (!analyticsHaveBeenAdded) addAnalytics();

    setUpObserversForTrackingSectionViews();

    function setUpObserversForTrackingSectionViews() {
      trackSectionViews(
        document.querySelector("[data-analytics-id='share-story']")
      );
      trackSectionViews(
        document.querySelector("[data-analytics-id='join-story-project']")
      );
      trackSectionViews(document.querySelector("[data-analytics-id='quotes']"));
      trackSectionViews(
        document.querySelector("[data-analytics-id='partners']")
      );

      function trackSectionViews(target) {
        const sectionName = target.dataset.analyticsId;
        const observerOptions = {
          root: null,
          threshold: 0.75,
        };

        const observer = new IntersectionObserver((entries) => {
          const landingAnalytics = JSON.parse(
            sessionStorage.getItem(analyticsBrowserStorageKey)
          );
          const entry = entries[0];
          const targetHasBeenViewed =
            landingAnalytics.sectionsViewedMap[sectionName];
          if (entry.isIntersecting && !targetHasBeenViewed) {
            ahoy.track(`web/root/view-section-${sectionName}`, {
              language: "JavaScript",
            });
            landingAnalytics.sectionsViewedMap[sectionName] = true;
            sessionStorage.setItem(
              analyticsBrowserStorageKey,
              JSON.stringify(landingAnalytics)
            );
          }
        }, observerOptions);

        observer.observe(target);
      }
    }

    function addAnalytics() {
      ahoy.track("web/root/landing-page-visited", {
        language: "JavaScript",
      });
      trackStoryBtnClicks();

      function trackStoryBtnClicks() {
        document.addEventListener("click", function (e) {
          if (e.target.classList.contains("cta")) {
            ahoy.track(`web/root/${e.target.dataset.analyticsId}-clicked`, {
              language: "JavaScript",
            });
          }
        });
      }

      const value = {
        analyticsHaveBeenAdded: true,
				sectionsViewedMap
      };
      sessionStorage.setItem(analyticsBrowserStorageKey, JSON.stringify(value));
    }
  }
</script>

Event naming convention

  • names for events on the website level should have this structure -> web/<page-name>/<event-name>
    • e.g. an event for tracking landing page visits can have a name like web/root/landing-page-visited
  • names for events on the API level should have this structure -> API/<API-namespace-name>/<event-name>
    • e.g. an event for tracking when someone views a form associated with blog API namespace can have a name like API/blog/form-viewed
  • during testing, all event names should be postfixed with -test
    • e.g. web/root/landing-page-visited-test
⚠️ **GitHub.com Fallback** ⚠️