Skip to content

Conversation

@maebeale
Copy link
Collaborator

@maebeale maebeale commented Feb 2, 2026

  • This work Closes [link an issue]

What is the goal of this PR and why is this important?

Add more events to ahoy activity tracking. Rearrange admin and activity-related files. Surface ahoy events on a new charts page.

- Model lifecycle Events:
    - create.<resource_name>
    - update.<resource_name>
    - destroy.<resource_name>
- Controller Events (from AhoyTracking):  
    - browse.taggings
    - download.resource
    - view.<resource_name>
    - print.<resource_name>
- Auth Events on User:  
    - auth.account_confirmed (triggered after devise confirmation)
    - auth.account_locked (triggered after devise account lock)
    - auth.account_unlocked (triggered after devise account unlock)
    - auth.email_changed (triggered when the email is changed)
    - auth.account_deleted (triggered before account deletion)
  • Move recent_activities out of dashboard and into Admin::AhoyActivitiesController
  • Update AhoyViewTracking -> AhoyTracking, since it's tracking more than just show page events now
  • Change activities/counts to cluster prints and downloads within the model square
  • Added model concern to have automatic callbacks to track_lifecycle_event without littering all controllers
    • This is separate from the track_event and track_index
  • Added callbacks to User for tracking auth events
  • Added method to track browse.taggings and also to capture search queries on Workshops and Resources
  • Added new /activities/charts endpoint that shows various charts based on ahoy data
    • This required adding chartkick, groupdate, geocoder, and maxmind-geoip2

How did you approach the change?

  • Added EventBuilder to construct/assign extra fields to ahoy events
  • Added ActivityPresenter to convert an ahoy Event to its corresponding record
  • Added current.rb to make user accessible to models (so it can be added to auth ahoy events)
  • Added already_tracked? to prevent duplicate activities from being created as Events within the same Visit
  • Added LifecycleBuffer to push and flush data from the store (to separate model-related ahoy data from models)
  • Added admin? method on user so we can move away from user.super_user designation later

Anything else to add?

  • Move /admin out of dashboard and into Admin::HomeController
  • Added a Rails.logger for when a notification is created
  • Bulk changed a bunch of rubocop violations

Copy link
Collaborator

@jmilljr24 jmilljr24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good stuff!

I see there is a start with the action_policy stuff. I can finish it off in another PR if its too much to include in this one.

- To see your data
- The home page will show Workshops, CommunityNews, Resources, Events, and Stories
- The [Admin Dashboard](http://localhost:3000/dashboard/admin) provides CRUD access for most models
- The [Admin Home](http://localhost:3000/admin) provides CRUD access for most models
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Admin Dashboard" wasn't accurate since it doesn't have any analytics on it. And also since we use the word Dashboard a lot wrt the old site, it seemed smart to change this to "Admin Home" instead.


it "denies access to analytics page" do
get "/admin/analytics"
get "/admin/activities/counts"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

analytics was a little too vague. this is really just rolling up ahoy event (user activity) counts.

gem "dotenv-rails"
gem "faker"
gem "factory_bot_rails"
gem "launchy"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
gem "launchy"

This is in group :test already.

@@ -0,0 +1,9 @@
class AddResourceDimensionsToAhoyEvents < ActiveRecord::Migration[8.1]
def change
add_column :ahoy_events, :resource_type, :string
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahoy by default stuffs everything into properties, but since we're querying and running charts, i wanted to pull some data into columns

get "contact_us", to: "contact_us#index"
post "contact_us", to: "contact_us#create"
get "dashboard/admin", to: "dashboard#admin"
get "dashboard/recent_activities", to: "dashboard#recent_activities"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these didn't belong in DashboardController. that should only run the site home page.

config/routes.rb Outdated
get "/", to: "home#index"
get "activities", to: "ahoy_activities#index", as: "activities"
get "activities/charts", to: "ahoy_activities#charts", as: "activities_charts"
get "activities/recent", to: "ahoy_activities#recent", as: "activities_recent"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still on the fence about /activities/recent since that content is now at /activities/. still might be good to have a separate, faster page that's just pulling most recent 50 activities.

@@ -0,0 +1,6 @@
Geocoder.configure(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is needed for the charts, incl the "Top Cities". maxmind requires you to store this mmdb file locally, so that's why it's referring to that file on line 4.

# customize Ahoy::Event to extract resource dimensions from properties
Rails.application.config.to_prepare do
Ahoy::Event.class_eval do
before_validation :extract_resource_dimensions, on: :create
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this callback is needed in order to populate those extra fields on ahoy events table


<%= render "tagged_items_carousel", items: items %>

<div class="mt-6 text-right">
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ux update while i was there. added a linked text in bottom right so it functions just like the site landing page.


</button>
<!-- Dropdown menu -->
<div
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is rearranging user dropdown. this must be coming from rebase.

@@ -15,30 +15,30 @@
<div class="row">
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all changes in this file are rubocop

@@ -19,7 +19,7 @@
<%= f.hidden_field :type %>
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rubocop changes in this file

<div class="flex-1 min-w-[220px]">
<%= f.input :category_type_id,
label: "Category Type",
required: true,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is required by validation but was missing from form

)


Rails.logger.info({
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

decided not to create ahoy events for notifications. so, adding to logs for visibility.

@@ -0,0 +1,22 @@
# app/services/analytics/lifecycle_buffer.rb
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is needed so we don't have to pollute models w ahoy logic

# app/services/analytics/event_builder.rb
module Analytics
class EventBuilder
def self.lifecycle(action, resource, user: nil)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is needed so we can append the custom data to our custom ahoy fields (resource_type, resource_id, resource_title)

end

def safe_for_tracking?(attribute)
!attribute.match?(/password|token|secret|key|digest|salt|otp/i)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't store these fields in ahoy

@maebeale
Copy link
Collaborator Author

maebeale commented Feb 3, 2026

yes please, @jmilljr24 !

@maebeale maebeale merged commit 783eec4 into main Feb 3, 2026
3 checks passed
@maebeale maebeale deleted the expand-ahoy branch February 3, 2026 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants