Skip to content

refactor(#126): move bounty filtering and sorting to server-side query#157

Merged
Benjtalkshow merged 4 commits intoboundlessfi:mainfrom
chiemezie1:feature/126-server-filtering-clean
Apr 23, 2026
Merged

refactor(#126): move bounty filtering and sorting to server-side query#157
Benjtalkshow merged 4 commits intoboundlessfi:mainfrom
chiemezie1:feature/126-server-filtering-clean

Conversation

@chiemezie1
Copy link
Copy Markdown
Contributor

@chiemezie1 chiemezie1 commented Mar 27, 2026

Title: refactor(#126): move bounty filtering and sorting to server-side GraphQL

Summary
This PR moves bounty filtering and sorting logic from client-side processing in app/bounty/page.tsx to server-driven query variables, improving scalability and consistency as bounty volume grows.

What Changed

  • Refactored app/bounty/page.tsx to pass filter/sort/pagination query variables into useBounties.
  • Replaced client-side sort logic with server-side sort parameter mapping:
    • newest -> sortBy=createdAt, sortOrder=desc
    • highest_reward -> sortBy=rewardAmount, sortOrder=desc
    • recently_updated -> sortBy=updatedAt, sortOrder=desc
  • Added debounced search input (500ms) to reduce request frequency.
  • Ensured filter/sort changes reset pagination state to page 1.

Scope of This Push
Only task-related code was pushed:

  • app/bounty/page.tsx

Validation Performed

  • Lint: npm run lint -- app/bounty/page.tsx (pass)
  • Targeted test path check: npm run test -- app/bounty --passWithNoTests (no tests found)

Notes

  • Push required bypassing local pre-push hook due environment-only optional dependency issue in @tailwindcss/oxide.
  • No unrelated docs or summary files were included in this branch.

Issue

Summary by CodeRabbit

  • New Features

    • Backend-driven querying with server pagination and total counts; “Previous/Next” pagination and page indicator.
    • Debounced search input and inline filter sidebar (Search, Status dropdown, Bounty Type accordion).
    • Clear/reset control to restore filters, sort, search, and pagination.
    • Improved loading, empty, and error states for results.
  • Chores

    • Type and status filters simplified to single-select.
    • Organization and reward-range filters shown as unsupported for now.

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 27, 2026

@chiemezie1 is attempting to deploy a commit to the Threadflow Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented Mar 27, 2026

@chiemezie1 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 27, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Replaced client-side filtering/sorting/pagination in the bounty listing with server-driven queries. Introduced local UI state (search/type/status/sort/page), debounced search, constructed a paginated BountyQueryInput sent to useBounties, updated rendering to consume backend pagination and added clear/reset and prev/next pagination controls.

Changes

Cohort / File(s) Summary
Bounty page (UI & query wiring)
app/bounty/page.tsx
Removed in-memory filtering/sorting (useMemo) and multi-select controls. Added debounced searchQuery, single-select selectedType/statusFilter, page state, memoized BountyQueryInput (page, limit:20, search, type, status, sort), passed it into useBounties(queryParams), consumed data.pagination and total counts, added Previous/Next pagination, clear/reset control, and replaced org/reward-range filters with unsupported notices.
Hook/API consumption (impacted areas)
hooks/use-bounties.ts, lib/graphql/.../bounties.graphql (related)
Page now expects useBounties to accept query variables and return paginated results (data, pagination, totalResults). Ensure hook/GraphQL accept and map filters/sort/pagination parameters used by the page.
UI components & states
app/bounty/... (inline filter UI within page)
Reworked UI from sidebar+toolbar+grid to an inline filter sidebar: Search input (debounced), Status dropdown, Bounty Type accordion; loading/error/empty/skeleton states updated to use backend responses.

Sequence Diagram(s)

sequenceDiagram
  participant User as User (Browser)
  participant Page as app/bounty/page.tsx
  participant Hook as useBounties
  participant API as GraphQL Server

  User->>Page: type search / change filters / change page / change sort
  Page->>Page: debounce search, build BountyQueryInput (page, limit, search, type, status, sort)
  Page->>Hook: call useBounties(queryParams)
  Hook->>API: GraphQL bounties(queryParams)
  API-->>Hook: paginated results (data, pagination: currentPage,totalPages,totalResults)
  Hook-->>Page: return paginated data
  Page->>User: render results, pagination controls, loading/error/empty states
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • 0xdevcollins

Poem

🐰 I nudged the search to whisper, not shout,

Filters now travel where servers sort out.
Pages arrive tidy, one hop at a time,
Debounced and polite — fetch after a rhyme.
Hooray for clean queries and fewer client climbs!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: moving bounty filtering and sorting from client-side to server-side query logic.
Linked Issues check ✅ Passed The PR successfully addresses all primary coding objectives from #126: eliminated client-side filtering/sorting, implemented server-side GraphQL querying with filter/sort/pagination parameters, and refactored the page component to use the backend-driven approach.
Out of Scope Changes check ✅ Passed All changes are scoped to app/bounty/page.tsx and directly support the stated objective of moving bounty filtering and sorting to server-side; no unrelated modifications are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/bounty/page.tsx`:
- Line 42: The component initializes pagination state with const [page, setPage]
= useState(1) but never advances it (all setPage calls reset to 1), so
server-side pagination (20 rows) prevents accessing subsequent pages; update the
component to pass page into the paginated hook (the hook in
hooks/use-bounties.ts that exposes paginated data) and implement proper controls
that call setPage(nextPage) / setPage(prevPage) (and not always setPage(1)) —
add Next/Previous (or page number) handlers that update page, wire those
handlers to the UI regions referenced around the existing page control code
(where setPage is currently used/reset) and ensure the paginated hook parameters
reflect the current page so rows beyond page 1 become reachable.
- Around line 98-124: The client-side reward/org filtering is only operating on
the paginated slice (allBounties) causing wrong counts and disappearing options;
remove the in-component filtering (filteredBounties) and instead extend the
backend query shape (BountyQueryInput in lib/query/bounty-queries.ts) and the
data hook (useBounties) to accept rewardMin/rewardMax and organizationIds
(array) parameters, then pass selectedOrgs and rewardRange from the page (where
allBounties, organizations, selectedOrgs, rewardRange are used) into useBounties
so filtering and faceting (organizations list) happen server-side and reflect
global results rather than the current page.
- Around line 67-74: The page currently treats types as multi-select
(selectedTypes/setSelectedTypes) but sends only selectedTypes[0] to the backend
(BountyQueryInput.type), causing dropped selections; change the UI and state to
a single-select flow: replace selectedTypes/setSelectedTypes with a single
selectedType state, update any components that set or read selectedTypes (the
selection handlers referenced around the other occurrences) to use selectedType,
and send selectedType (as BountyType) in the query object instead of
selectedTypes[0]; ensure references to selectedTypes in the file (including the
blocks around lines 126-129 and 135-140) are updated to the new single-select
symbol so the GraphQL backend (which expects a singular type) receives the
correct value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 09575c85-aa38-4d3f-b4e5-ed890b7e296f

📥 Commits

Reviewing files that changed from the base of the PR and between 3aad250 and 2cb5bd9.

📒 Files selected for processing (1)
  • app/bounty/page.tsx

Comment thread app/bounty/page.tsx
Comment thread app/bounty/page.tsx Outdated
Comment thread app/bounty/page.tsx Outdated
Copy link
Copy Markdown
Contributor

@Benjtalkshow Benjtalkshow left a comment

Choose a reason for hiding this comment

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

Hi @chiemezie1 , good initiative on moving filtering and sorting to the server side,
The critical issue is that pagination controls are completely missing. page is initialized to 1 and only ever reset to 1, so with limit: 20, users can never see bounties beyond the first page. The result count (filteredBounties.length) also misleadingly shows the client-filtered count of the current page slice rather than the total matching results from the server.

Please address the following:

Change the type filter from multi-select to single-select. The backend only supports one type, so only selectedTypes[0] is sent and the rest are silently dropped.

Move reward range and organization filtering to the server side, or clearly document them as known limitations. Right now they only filter the 20-item page slice, which causes false empty states and disappearing filter options when matching bounties exist on other pages.

Fix the CodeRabbit corrections mentioned above and resolve any merge conflicts and CI failure in this PR .

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
app/bounty/page.tsx (1)

214-218: Use radio semantics for the now-single-select type filter.

Only one BountyType can be active, but Lines 214-218 still render independent checkboxes. Screen readers will announce these as multi-select toggles, which doesn't match the behavior. A radio group or single Select would expose the constraint correctly. Please verify the keyboard and screen-reader flow after swapping the control.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/bounty/page.tsx` around lines 214 - 218, The UI uses Checkbox components
for a single-select filter (rendered in the map that uses selectedType and
toggleType), which misleads assistive tech; replace the Checkbox at the site
where id={`type-${type.value}`} is rendered with a radio-style control (either a
Radio component or a single Select) so the semantics reflect single selection,
wire its checked/selected state to selectedType, call toggleType(type.value) (or
a renamed onChange handler) to update selection, and ensure the group has
appropriate role/aria-labeling (radio group semantics) and keyboard navigation;
verify keyboard and screen-reader flow after the swap.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/bounty/page.tsx`:
- Around line 109-115: clearFilters currently sets status back to
BountyStatus.Open which leaves out completed/cancelled bounties; either change
the reset to a true "all" value (e.g., setStatusFilter("all") or a
BountyStatus.All enum) and update any filter-check logic that treats OPEN as the
no-filter baseline (references: clearFilters, setStatusFilter,
BountyStatus.Open), or if OPEN is intentional keep the code but change the
UI/action copy from "Clear filters" to "Reset to defaults" so it accurately
reflects that completed/cancelled items remain excluded; make the corresponding
change where the status baseline is evaluated (the code that checks OPEN as no
active filters) so behavior and wording remain consistent.
- Around line 63-84: The bug is that queryParams uses debouncedSearchQuery so
clearing the textbox empties searchQuery immediately but the backend filter
lingers until useDebounce updates; update the query construction to drop the
search filter immediately when searchQuery is an empty string (e.g. use a
conditional like ...(searchQuery === "" ? {} : debouncedSearchQuery && { search:
debouncedSearchQuery })) so that clearing the input removes the search param
right away; reference the variables useDebounce, debouncedSearchQuery,
searchQuery and the queryParams object (and keep getSortParams unchanged).

---

Nitpick comments:
In `@app/bounty/page.tsx`:
- Around line 214-218: The UI uses Checkbox components for a single-select
filter (rendered in the map that uses selectedType and toggleType), which
misleads assistive tech; replace the Checkbox at the site where
id={`type-${type.value}`} is rendered with a radio-style control (either a Radio
component or a single Select) so the semantics reflect single selection, wire
its checked/selected state to selectedType, call toggleType(type.value) (or a
renamed onChange handler) to update selection, and ensure the group has
appropriate role/aria-labeling (radio group semantics) and keyboard navigation;
verify keyboard and screen-reader flow after the swap.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ca10c3b8-24df-4d93-bdd1-f22510f00faa

📥 Commits

Reviewing files that changed from the base of the PR and between 2cb5bd9 and 1283192.

📒 Files selected for processing (1)
  • app/bounty/page.tsx

Comment thread app/bounty/page.tsx Outdated
Comment thread app/bounty/page.tsx
@chiemezie1 chiemezie1 requested a review from Benjtalkshow March 31, 2026 03:36
@chiemezie1
Copy link
Copy Markdown
Contributor Author

Hi @Benjtalkshow, thanks again for the review. I’ve pushed updates to address your feedback:

  • Added working pagination controls (Previous/Next) so users can navigate beyond page 1.
  • Updated the results count to use the server total, not the current page slice.
  • Changed bounty type filtering to single-select to match backend support.
  • Removed misleading client-side reward/org filtering and added a clear note that these filters are temporarily unavailable until backend support is added.
  • Applied the CodeRabbit-requested fixes and resolved the TS/CI issue from the previous version.
  • One remaining check is Vercel, but it’s an authorization issue on the team/project side (not a code failure). Please take another look when you have time.

- Drop search filter immediately on clear instead of waiting for debounce
- Reset statusFilter to "all" in clearFilters (was resetting to OPEN)
- Align filter-active baseline check with the new "all" default
- Memoize queryParams to stabilize the useBounties query key
- Hoist BOUNTY_TYPES, STATUSES, and getSortParams to module scope

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

@Benjtalkshow Benjtalkshow left a comment

Choose a reason for hiding this comment

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

LGTM!

@Benjtalkshow Benjtalkshow merged commit 56a242b into boundlessfi:main Apr 23, 2026
0 of 2 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.

Move Bounty Filtering and Sorting to Server-Side (GraphQL)

2 participants