8000 feat(groups): personal sections + search on /groups by mwoods79 · Pull Request #157 · huddlz-hq/huddlz · GitHub
[go: up one dir, main page]

Skip to content

feat(groups): personal sections + search on /groups - #157

Merged
mwoods79 merged 6 commits into
mainfrom
feat/groups-personal-sections
May 2, 2026
Merged

mwoods79 merged 6 commits into
mainfrom
feat/groups-personal-sections

Conversation

@mwoods79
@mwoods79 mwoods79 commented May 2, 2026
Copy link
Copy Markdown
Contributor

Summary

Reworks /groups so authenticated users can find their own groups without scanning the full directory, and adds the search bar that was missing.

What's new on /groups

  • // HOSTING (n) section — groups you own
  • // JOINED (n) section — groups you belong to but don't own (owners are filtered out so they don't double-count, since owners are auto-added as members on creation)
  • Each section caps at 6 cards and shows View all → when count > 6, deep-linking to /groups?yours=hosting or /groups?yours=joined
  • Search input (trigram similarity, matching the :search action's behavior) filters all three queries in unison
  • Empty sections are hidden entirely — anonymous users and users with no relationships see the original directory layout
  • The View all link preserves the active search via &q=
  • Scoped views (?yours=...) render a single full grid with an ← All groups back link and a context-appropriate page title

API surface

Per the API-first principle: the new behavior is reachable as a real action.

  • :get_joined read action on Group — filters by exists(group_members, user_id == ^actor(:id)) and owner_id != ^actor(:id)
  • Communities.get_joined_groups
  • GraphQL: joined_groups
  • JSON API: /groups/joined

Trade-offs worth flagging

  • Cards appear in two places — a group you host shows up in // HOSTING and the // All Groups grid below. Standard pattern (Meetup, GitHub) and intentional: sections are a shortcut, not a filter.
  • Three queries per render when logged in (hosting, joined, all). Trigram + actor-scoped filters are cheap; revisit if it shows up in metrics.
  • No pagination on the scoped views yet. Most users won't have hundreds of groups owned/joined; can add if needed.

Out of scope

  • The same treatment for /huddlz — natural follow-up, would mirror this structure with "Hosting upcoming" and "Attending upcoming" sections. Time-bounded data so the sections cap themselves.

Test plan

  • /groups as anonymous — no personal sections, full directory grid, search input visible
  • /groups logged in with no relationships — same as anonymous (no // Hosting / // Joined headings)
  • /groups logged in as owner — // Hosting section appears with your group; not duplicated under // Joined
  • /groups logged in as member of someone else's group — // Joined section appears with that group
  • Type a search query — sections and main grid filter together; non-matching sections disappear; clear button appears
  • /groups?yours=hosting — single grid, page title "Groups You Host", back link works
  • /groups?yours=joined — single grid, page title "Groups You've Joined"
  • Create 7+ owned groups, visit /groups — View all → link visible in // Hosting, navigates to /groups?yours=hosting
  • Search active + click View all — destination URL preserves &q=

mwoods79 added a commit that referenced this pull request May 2, 2026
Fills the testing gaps flagged in PR #157:

- LiveView: assert View-all link's actual href (not just text), and
  that it preserves the active search query
- LiveView: search with no matches collapses both personal sections
  and shows the empty-state message
- Resource: direct tests on :get_joined (returns joined groups,
  excludes owned groups, excludes unrelated groups, empty for users
  with no memberships)
- GraphQL: joinedGroups query returns the right groups for the
  authenticated actor; empty for users with no memberships
- Cucumber: feature for the headline flow (sections appear, anonymous
  users see no sections, View-all navigates to scoped view, search
  filters everything together)
mwoods79 added 6 commits May 2, 2026 00:53
Adds a read action that returns groups the actor has joined but does
not own. Owners are filtered out so the action complements :get_by_owner
without overlap (since owners are auto-added as members on creation).

Exposed via domain wrapper, GraphQL (joined_groups), and JSON API at
/groups/joined for parity with :get_by_owner.
Reworks /groups so authenticated users can quickly find their own
groups without scanning the full directory.

- Two personal sections at the top: "// Hosting" and "// Joined"
  rendered when the user has groups in that relationship
- Each section caps at 6 cards and shows "View all →" when there
  are more, deep-linking to /groups?yours=hosting|joined
- Search input (trigram similarity) filters all three queries in
  unison; clear control appears when query is non-empty
- ?yours=hosting and ?yours=joined render a single full grid scoped
  to that relationship with a back link
- Search query is preserved across the View all transition

Hidden sections collapse cleanly: anonymous users and users with no
relationships see the original directory layout unchanged.
Fills the testing gaps flagged in PR #157:

- LiveView: assert View-all link's actual href (not just text), and
  that it preserves the active search query
- LiveView: search with no matches collapses both personal sections
  and shows the empty-state message
- Resource: direct tests on :get_joined (returns joined groups,
  excludes owned groups, excludes unrelated groups, empty for users
  with no memberships)
- GraphQL: joinedGroups query returns the right groups for the
  authenticated actor; empty for users with no memberships
- Cucumber: feature for the headline flow (sections appear, anonymous
  users see no sections, View-all navigates to scoped view, search
  filters everything together)
The :hosting and :joined scopes returned a literal "You aren't hosting
any groups yet." regardless of whether a search query was active, so a
user who hosts groups but searched for something that doesn't match saw
copy implying they host nothing.

Empty copy now branches on the search query first; the per-scope copy
only runs when there's no active search.
< 8000 div data-view-component="true" class="TimelineItem TimelineItem--condensed">
- scoped_path now produces a stable `?yours=&q=` parameter order
  matching view_all_path, so navigating between the search input and
  View all no longer flickers the URL ordering
- read_groups logs an Ash.read failure at warning level instead of
  silently returning [], so a permission/policy regression surfaces
  in logs rather than as a mysteriously empty section
- Renames apply_search/read_groups parameters to ash_query so the
  Ash.Query and the user search string don't share the name `query`
- Comment on list_all explains the deliberate public-only filter
- Pipe convention nit on normalize_query
Anonymous users hitting /groups?yours=hosting or ?yours=joined directly
saw a "Groups You Host" page with an empty state — discoverable but
nonsensical. Redirect to /sign-in with a flash explaining what they
were trying to see.

The /groups index itself stays public; only the personal-scoped views
require auth.
@mwoods79
mwoods79 force-pushed the feat/groups-personal-sections branch from 22b7b4c to 40f81b3 Compare May 2, 2026 04:53
@mwoods79
mwoods79 merged commit 7817a3c into main May 2, 2026
1 check passed
mwoods79 added a commit that referenced this pull request May 2, 2026
Fills the testing gaps flagged in PR #157:

- LiveView: assert View-all link's actual href (not just text), and
  that it preserves the active search query
- LiveView: search with no matches collapses both personal sections
  and shows the empty-state message
- Resource: direct tests on :get_joined (returns joined groups,
  excludes owned groups, excludes unrelated groups, empty for users
  with no memberships)
- GraphQL: joinedGroups query returns the right groups for the
  authenticated actor; empty for users with no memberships
- Cucumber: feature for the headline flow (sections appear, anonymous
  users see no sections, View-all navigates to scoped view, search
  filters everything together)
@mwoods79
mwoods79 deleted the feat/groups-personal-sections branch May 2, 2026 04:54
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

0