Skip to content

Convert events and visits to dynamic Turbo Frame search#1349

Open
maebeale wants to merge 24 commits intomainfrom
maebeale/dynamic-search-events-visits
Open

Convert events and visits to dynamic Turbo Frame search#1349
maebeale wants to merge 24 commits intomainfrom
maebeale/dynamic-search-events-visits

Conversation

@maebeale
Copy link
Collaborator

@maebeale maebeale commented Mar 6, 2026

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

  • Admin events and visits pages used full-page-reload form submissions for filtering, making them slow and cumbersome to use
  • Converts both pages to use the same dynamic Turbo Frame + Stimulus collection controller pattern already used by stories
  • Incorporates the resource name, props, and event name filters from Add visit ID and props filters to activity events #1348

How did you approach the change?

  • Split each page into a shell (form + turbo frame) and lazy-loaded results partial, matching the stories pattern
  • Controller branches on turbo_frame_request? — renders shell on initial load, results partial on frame requests
  • Extracted filtering logic into apply_event_filters / apply_visit_filters private methods
  • Added sortable column headers (time/name/user for events, started_at/user/events_count for visits)
  • Text inputs auto-submit with debounce via the existing collection Stimulus controller
  • Select/checkbox inputs auto-submit immediately on change
  • Added skeleton loaders for loading state and blur-on-submit UX
  • Added filtered/total count display (e.g. "23/150")
  • Added filter badges to the active filters subheader for event name, resource name, visit ID, and props

UI Testing Checklist

  • Navigate to admin Activities > Events — skeleton loads, then results appear
  • Type in Event Name — results filter dynamically after debounce
  • Type in Resource Name — filters by resource_title in properties JSON
  • Type in Props — searches across full properties JSON blob
  • Enter Visit ID — filters to that specific visit
  • Change User dropdown — immediate re-filter
  • Change Audience checkboxes — immediate re-filter
  • Change Time Period dropdown — immediate re-filter
  • Set From/To dates — filters by date range
  • Click sortable column headers (Time, Event, User) — sorts within frame
  • Click pagination — stays within frame
  • Click "Clear filters" — resets all inputs and reloads
  • Navigate to Visits tab — same dynamic behavior
  • Sort visits by Started, User, Events count
  • Combine multiple filters — all work together
  • Filter badges appear in subheader on charts page when filters are active

Anything else to add?

🤖 Generated with Claude Code

maebeale and others added 24 commits March 6, 2026 06:01
Adds three new search fields to the admin activities events page:
- Resource Name: text search on resource_title in event properties JSON
- Visit ID: integer filter on visit_id (backend existed, adds UI)
- Props: full-text search across the entire properties JSON blob

Also adds filter badges to the active filters subheader and preserves
new params in quick range links.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace full-page-reload filtering with the same Turbo Frame + Stimulus
collection controller pattern used by stories. Filters now auto-submit
with debounce on text input and immediate submit on select/checkbox
changes. Adds sortable columns, skeleton loaders, and filtered/total
count display. Incorporates resource name, props, and event name filters
from #1348.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Match the counts/charts layout by placing audience dropdown and time
period select in the top-right header area with their own form. Search
filter form carries these values as hidden fields so turbo frame
requests preserve them.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switches from JSON_UNQUOTE(JSON_EXTRACT(...)) to the ->> shorthand
for more reliable JSON path extraction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve merge conflicts by keeping the dynamic search refactor and
adopting the MySQL ->> shorthand operator for JSON extraction from
the parent branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap active_filters_subheader in an identifiable div so turbo stream
can replace it on each search. Search box filters (event name, resource,
visit ID, props, user) now appear as chips alongside the existing time
period and audience badges. Also add user filter badge.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Date inputs have type="date" which the collection controller doesn't
handle, so add explicit onchange to trigger form submission when a
date is picked.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add date to the change handler and number to the input handler so the
Stimulus controller auto-submits for all input types. Remove redundant
inline oninput/onchange attributes from the views. Also clear date and
number fields in clearAndSubmit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove trailing commas left after inline oninput/onchange attributes
were removed, which caused ActionView::SyntaxErrorInTemplate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Chip order: audience → time period → from/to dates → search filters
- Move date fields to left of search form (next to time period)
- Add X buttons on all chips to dynamically remove filters
- New filter_chip Stimulus controller handles chip dismissal

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Date fields: "Start date" / "End date" placeholders
- User dropdown: "Select user..." prompt
- Add focus ring classes to date fields for consistency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uses CSS :has() to detect when the empty-value option is checked
and applies grey text color, matching text input placeholder styling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… CSS

- Remove custom CSS :has() rules
- Add text-gray-400/text-gray-900 toggle in collection controller
- Conditionally apply text-gray-400 in ERB for initial render

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change select and date placeholders from text-gray-400 to text-gray-500
  to match browser default placeholder color in text inputs
- Add text-gray-500 conditional class to date inputs when empty
- Generalize stylePlaceholder() in collection controller for both
  selects and date inputs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uses opacity on ::-webkit-calendar-picker-indicator to match
the grey placeholder text styling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the .text-gray-500 condition so the icon stays grey
regardless of whether a date is selected.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Date selectors: flex-[1.3] → flex-[1.1] (slightly thinner)
- Visit ID: flex-1 → flex-[0.7] (thinner)
- Resource Title: flex-1 → flex-[1.2] (wider), renamed from Resource Name
- Update table column header and filter chip label to match

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Browsers often ignore autocomplete=off on the form tag, especially
for fields named 'name'. Adding it to each input prevents autofill.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
LastPass ignores data-lpignore on individual inputs when it
detects a 'name' field. Adding these attributes at the form
level tells LastPass this is not a login/identity form.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant