ReviewOS

stacks/bench-review

discovery: search filters on judges and courthouses pages

#30
Closed glennmichael123 opened this 10 days ago ·
10 days ago

/judges and /court-houses show unfiltered lists. State, practice area, rating threshold, "has reviews" — these are the standard axes a user expects.

Scope

  • /judges filter pills + URL-driven state:
    • state (CA / NY / ...) — derived from court_houses.state via the belongsTo
    • practice_area — uses the existing Judge.practice_area column
    • rating threshold (>= 4 stars / >= 3 stars / ...) — derived aggregate
    • has-reviews / no-reviews — derived boolean
  • /court-houses filter pills: state, has-reviews
  • API: extend JudgeIndexAction / CourtHouseIndexAction to accept these query params and apply filters in the underlying query.
  • UI: same pill pattern MyReviewsView already uses for status, URL via ?state=CA&practice_area=appellate.

Acceptance

  • Filter pills shrink the listing in-place without full page reload.
  • Active filters persist in the URL for deep-linkable / shareable searches.
  • Filter counts ("42 judges in CA") show on the pill labels.
  • Best done after the Meilisearch migration so the filters use the filterable-attributes feature instead of SQL WHERE.
9 days ago

Shipped — full filter pill rows on both directory pages, URL-driven so deep links survive.

`/judges` (`JudgeDirectory.stx`):

  • Practice area (from #38, already shipped): All / Criminal / Civil / Family / Probate / Appellate / Bankruptcy / Other.
  • State (new): auto-populated from the loaded judges' courthouse rows — only surfaces states that actually have judges to filter to. All states / CA / NY / DC / …
  • Rating threshold (new): Any / 4★ and up / 3★ and up / 2★ and up.
  • Activity (new): Any / With reviews / Without reviews.
  • All four axes compose with each other AND with the existing text-search box. URL params: `?practice_area=family&state=CA&rating=4&has_reviews=with`. Empty/default params get stripped from the URL.

`/court-houses` (`CourtHouseDirectory.stx`):

  • State (new): auto-populated, same pattern as judges.
  • Activity (new): Any / With reviews / Without reviews — uses the `total_reviews` aggregate from #40.
  • Both compose with the text-search box. URL params: `?state=CA&has_reviews=with`.

Plumbing:

  • Live counts on every pill ("California (4)" / "With reviews (12)") derived from the unfiltered roster so they reflect the full directory, not the current filtered view.
  • `replaceState` (not push) on every pill click so rapid tapping doesn't bloat the back stack.
  • Initial state read from `URLSearchParams` on mount — deep links and shares preserve the filter context.
  • Same pill visual pattern as `MyReviewsView` (status filter) and the practice-area row from #38: active = solid black, inactive = white with hover border.

Removed: the unwired `<select>` placeholders ("All Courts", "All States", "Rating") on both pages that were just visual decoration. Replaced with the real pill rows.

Server-side filtering is the right next move once the Meilisearch migration (#41 Phase 5) lands — these pills become Meili facet filter expressions on the existing index settings, and the JS-side composition logic gets retired. For current bench-review scale (sub-100 judges, sub-50 courts in the seeded data) client-side filtering is fine.

Sign in to comment on this issue.