Skip to content

Group sub-locations under target locations#4127

Merged
mo-nathan merged 6 commits intomainfrom
njw-4126-group-sub-locations
Apr 16, 2026
Merged

Group sub-locations under target locations#4127
mo-nathan merged 6 commits intomainfrom
njw-4126-group-sub-locations

Conversation

@mo-nathan
Copy link
Copy Markdown
Member

@mo-nathan mo-nathan commented Apr 15, 2026

Summary

  • Switch candidate observation matching from GPS bounding box to name-suffix matching (e.g., "Chatfield Hollow State Park, Connecticut, USA" matches target "Connecticut, USA"), fixing cross-state overlap issues
  • Add collapsible chevron UI on the Locations tab to group sub-locations under their target locations with aggregated observation counts
  • Locations not matching any target appear in a separate section below
  • Add include_sub_locations param to Checklist so target location links show species from all sub-locations
  • Extract shared Projects::LocationGrouping concern for reuse between LocationsController and TargetLocationsController

Fixes #4126

Test plan

  • Visit a project with target locations — verify targets appear with chevron toggles
  • Click chevron — sub-locations expand/collapse
  • Verify observation counts aggregate correctly (target count = direct + sub-location obs)
  • Click target location link — checklist shows species from all sub-locations
  • Click sub-location link — checklist shows species from that location only
  • Visit a project without target locations — verify flat location list renders as before
  • Add/remove a target location — verify Turbo Stream update renders correctly
  • Full test suite passes (4911 tests, 0 failures)

🤖 Generated with Claude Code

Switch from GPS bounding box to name-suffix matching for candidate
observations and location grouping. Target locations now appear with
collapsible chevron toggles showing aggregated observation counts.
Locations not matching any target appear in a separate section below.

Fixes #4126

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 15, 2026 01:46
@coveralls
Copy link
Copy Markdown
Collaborator

coveralls commented Apr 15, 2026

Coverage Status

coverage: 96.276% (+0.01%) from 96.266% — njw-4126-group-sub-locations into main

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates project location handling to group observed sub-locations under configured target locations using name-suffix matching (instead of GPS bounding boxes) and updates the Locations tab UI to support collapsible target groups with aggregated observation counts, plus a checklist option to include sub-locations.

Changes:

  • Switch candidate observation location matching from bounding-box matching to location-name suffix matching (with optional project bounding-box constraint).
  • Add grouped/collapsible target-location rendering on the Project “Locations” tab with aggregated per-location observation counts.
  • Extend project checklist generation to optionally include sub-locations when viewing a target location.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
test/models/location_test.rb Adds coverage for new Location.sub_locations_of scope.
test/controllers/projects/locations_controller_test.rb Adds controller tests for presence/absence of target-location grouping UI.
test/components/projects/locations_table_test.rb Updates component tests for grouped/ungrouped data + chevron/count behavior.
app/views/controllers/projects/target_locations/_locations_update.erb Updates Turbo Stream partial to render grouped/ungrouped location tables and counts.
app/views/controllers/projects/locations/index.html.erb Updates index view to use grouped/ungrouped data and counts.
app/models/project.rb Updates candidate location matching to use name-suffix conditions (plus optional project box).
app/models/location/scopes.rb Introduces sub_locations_of suffix-matching scope.
app/controllers/projects/target_locations_controller.rb Refactors update rendering to use shared grouping concern.
app/controllers/projects/locations_controller.rb Refactors index to use shared grouping concern + observation counts.
app/controllers/concerns/projects/location_grouping.rb New shared grouping/counting logic for target/sub-location organization.
app/controllers/checklists_controller.rb Adds request param handling to include sub-locations in project checklists.
app/components/projects/locations_table.rb Implements grouped/collapsible rendering + observation count column.
app/classes/checklist.rb Adds include_sub_locations option for project checklists.

Comment thread app/components/projects/locations_table.rb
Comment thread app/models/project.rb Outdated
Comment thread app/models/location/scopes.rb Outdated
Comment thread app/classes/checklist.rb Outdated
Comment thread app/classes/checklist.rb
Comment thread app/controllers/concerns/projects/location_grouping.rb Outdated
Comment thread app/controllers/concerns/projects/location_grouping.rb Outdated
Comment thread app/components/projects/locations_table.rb
mo-nathan and others added 3 commits April 15, 2026 08:44
- Escape SQL LIKE wildcards with sanitize_sql_like in all
  suffix-matching queries (project.rb, scopes.rb, checklist.rb)
- Add text-decoration:none to .panel-collapse-trigger CSS
- Add CSS override for tbody.collapse.in to use table-row-group
  display instead of Bootstrap's default block
- Assign sub-locations to most specific (longest name) target to
  prevent duplicates when targets overlap
- Add test for include_sub_locations checklist behavior
- Add global outline-offset:2px for focus outlines on interactive
  elements so they don't clip text/icons

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Narrow the inset box-shadow focus override from all .panel-body
to just .rss-box-details so that checklist and other panel pages
use the outline-offset approach like the project locations tab.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test exercises assign_to_targets, most_specific_target, and
ungrouped filtering by creating a project with California as a
target, Albion as a sub-location, and NYBG as an ungrouped location.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates how project locations and target locations are matched and displayed by switching target matching from GPS bounding boxes to name-suffix matching, and adds a grouped/collapsible UI on the Project “Locations” tab to nest observed sub-locations under their target locations (with aggregated observation counts).

Changes:

  • Add name-suffix matching for “sub-locations” (e.g., ..., <Target Location Name>) and reuse it for candidate observation matching and checklists.
  • Introduce shared Projects::LocationGrouping concern to build grouped/ungrouped location collections and observation counts for controllers/views.
  • Update the Locations tab UI to render collapsible target groups with aggregated counts, plus an ungrouped section.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
app/models/project.rb Switch candidate location matching from GPS-box-based to location-name-suffix matching (with optional project bounding box constraint).
app/models/location/scopes.rb Add sub_locations_of scope based on location-name suffix.
app/classes/checklist.rb Add include_sub_locations option for project checklists using suffix matching.
app/controllers/concerns/projects/location_grouping.rb New concern to group observed locations under targets and compute per-location observation counts.
app/controllers/projects/locations_controller.rb Use LocationGrouping to supply grouped/ungrouped data + counts to the Locations view.
app/controllers/projects/target_locations_controller.rb Use LocationGrouping for Turbo updates after target location changes.
app/controllers/checklists_controller.rb Wire request param to Checklist::ForProject#include_sub_locations.
app/components/projects/locations_table.rb Render grouped target rows with collapsible sub-location <tbody> sections and an observations-count column.
app/views/controllers/projects/locations/index.html.erb Pass grouped/ungrouped datasets and counts into the LocationsTable component.
app/views/controllers/projects/target_locations/_locations_update.erb Update Turbo-streamed table render to the new component API.
app/assets/stylesheets/mo/_icons.scss Add collapse/table display overrides and trigger styling for the chevron toggle UI.
app/assets/stylesheets/mo/_elements.scss Improve focus outline spacing and provide visible focus styling inside .rss-box-details.
test/models/location_test.rb Add coverage for Location.sub_locations_of.
test/models/checklist_test.rb Add coverage for project checklist include_sub_locations option.
test/controllers/projects/locations_controller_test.rb Add coverage for grouping behavior and target/no-target rendering.
test/components/projects/locations_table_test.rb Update component tests for grouped/ungrouped inputs, chevrons, and aggregated counts.

Comment thread app/models/project.rb
Comment thread app/classes/checklist.rb
Comment thread test/models/checklist_test.rb
mo-nathan and others added 2 commits April 15, 2026 09:59
Memoize is_admin? result in LocationsTable component to eliminate
redundant queries (one per table row). Add controller test that
exercises location grouping with sub-locations and ungrouped
locations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test creates an observation at a Nevada location with GPS coords
inside California's bounding box, then verifies it appears with
GPS-box matching but is correctly excluded by name-suffix matching.
This is the core behavior fix from #4126.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mo-nathan mo-nathan merged commit b21eab9 into main Apr 16, 2026
9 checks passed
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.

Group sub-locations on the Project Locations tab

3 participants