refactor: modularize BountiesPage and fix status filter bug (#127)#165
refactor: modularize BountiesPage and fix status filter bug (#127)#165KaruG1999 wants to merge 1 commit intoboundlessfi:mainfrom
Conversation
|
@KaruG1999 is attempting to deploy a commit to the Threadflow Team on Vercel. A member of the Team first needs to authorize it. |
|
@KaruG1999 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! 🚀 |
📝 WalkthroughWalkthroughMoved bounty filtering, sorting, and UI responsibilities out of the monolithic page into a new hook and reusable components: Changes
Sequence Diagram(s)sequenceDiagram
participant Page as app/bounty/page.tsx
participant Hook as useBountyFilters
participant Sidebar as FiltersSidebar
participant Toolbar as BountyToolbar
participant Grid as BountyGrid
Page->>Hook: call useBountyFilters(allBounties)
Hook-->>Page: return { filteredBounties, state, setters, hasActiveFilters }
Page->>Sidebar: render with state + setters (search/toggle/org/reward/status/clear)
Page->>Toolbar: render with totalCount + sort state + onSortChange
Page->>Grid: render with filteredBounties, isLoading, isError, onRetry, onClearFilters
Sidebar->>Hook: (via props) invoke onSearch/onToggle/onReward/onClear
Toolbar->>Hook: (via props) invoke onSortChange
Grid->>Page: onClearFilters / onRetry callbacks propagate back to Hook/Page
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
components/bounty/bounty-toolbar.tsx (1)
11-15: Consider using a union type forsortOptionto improve type safety.Using a string union type instead of
stringwould catch invalid sort values at compile time and provide better IDE autocompletion.♻️ Proposed type improvement
+type SortOption = "newest" | "highest_reward" | "recently_updated"; + interface BountyToolbarProps { totalCount: number; - sortOption: string; - onSortChange: (value: string) => void; + sortOption: SortOption; + onSortChange: (value: SortOption) => void; }This type could be exported from
use-bounty-filters.tsand reused across components.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/bounty/bounty-toolbar.tsx` around lines 11 - 15, Replace the loose string type for sortOption in BountyToolbarProps with a string union of the allowed sort keys (e.g., 'newest' | 'oldest' | 'mostPopular' etc.), export that union type from use-bounty-filters.ts (e.g., BountySortOption) and import it into components/bounty/bounty-toolbar.tsx; update the prop signature onSortChange to accept the union type (onSortChange: (value: BountySortOption) => void) so both the prop and any callers are type-safe and get IDE autocomplete for valid sort values.components/bounty/filters-sidebar.tsx (1)
190-199: Remove redundantdefaultValueon controlled Slider.The
Sliderhas bothdefaultValueandvalueprops. Sincevalueis provided, this is a controlled component anddefaultValueis ignored.♻️ Remove redundant prop
<Slider - defaultValue={[0, 5000]} max={5000} step={100} value={[rewardRange[0], rewardRange[1]]}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/bounty/filters-sidebar.tsx` around lines 190 - 199, The Slider is currently controlled (it uses the value prop), so remove the redundant defaultValue prop; locate the Slider component in filters-sidebar.tsx (the JSX using Slider with value={[rewardRange[0], rewardRange[1]]} and onValueChange calling onRewardRangeChange) and delete the defaultValue={[0, 5000]} attribute, leaving the controlled value and onValueChange logic (keep the existing fallback val[1] ?? 5000 if desired).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@hooks/use-bounty-filters.ts`:
- Around line 59-60: The status comparison in matchesStatus fails due to case
mismatch between statusFilter (lowercase) and bounty.status (uppercase); update
the comparison so both sides use the same case—either normalize statusFilter to
uppercase when storing it via setStatusFilter or compare bounty.status to an
uppercased statusFilter in the matchesStatus expression (i.e., use statusFilter
transformed to uppercase), referencing the statusFilter variable, the
bounty.status property, the matchesStatus expression, and the setStatusFilter
setter to locate the code.
---
Nitpick comments:
In `@components/bounty/bounty-toolbar.tsx`:
- Around line 11-15: Replace the loose string type for sortOption in
BountyToolbarProps with a string union of the allowed sort keys (e.g., 'newest'
| 'oldest' | 'mostPopular' etc.), export that union type from
use-bounty-filters.ts (e.g., BountySortOption) and import it into
components/bounty/bounty-toolbar.tsx; update the prop signature onSortChange to
accept the union type (onSortChange: (value: BountySortOption) => void) so both
the prop and any callers are type-safe and get IDE autocomplete for valid sort
values.
In `@components/bounty/filters-sidebar.tsx`:
- Around line 190-199: The Slider is currently controlled (it uses the value
prop), so remove the redundant defaultValue prop; locate the Slider component in
filters-sidebar.tsx (the JSX using Slider with value={[rewardRange[0],
rewardRange[1]]} and onValueChange calling onRewardRangeChange) and delete the
defaultValue={[0, 5000]} attribute, leaving the controlled value and
onValueChange logic (keep the existing fallback val[1] ?? 5000 if desired).
🪄 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: d1e6113d-f470-4ccd-b447-37cbda7df524
📒 Files selected for processing (6)
app/bounty/page.tsxcomponents/bounty/bounty-grid.tsxcomponents/bounty/bounty-toolbar.tsxcomponents/bounty/filters-sidebar.tsxhooks/__tests__/use-bounty-filters.test.tshooks/use-bounty-filters.ts
8da94ab to
a895f0b
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
app/bounty/page.tsx (1)
37-53: Narrow theFiltersSidebarprop surface.This keeps
page.tsxsmaller, but the sidebar still needs a large number of tightly related props. Every new filter now expands both sides of the boundary. Consider grouping these into typedvalues,options, andactionsobjects so the composition layer stays stable as filters grow.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/bounty/page.tsx` around lines 37 - 53, The FiltersSidebar prop list is too wide; refactor page.tsx and FiltersSidebar to accept three grouped props (e.g., values, options, actions) instead of many individual props: move current state read-only items (filters.searchQuery, filters.selectedTypes, filters.selectedOrgs, filters.rewardRange, filters.statusFilter, filters.organizations) into a single values object, move static choices (BOUNTY_TYPES, STATUSES) into an options object, and move callbacks (filters.setSearchQuery, filters.toggleType, filters.toggleOrg, filters.setRewardRange, filters.setStatusFilter, filters.clearFilters, filters.hasActiveFilters) into an actions object; update the FiltersSidebar component signature and its prop types to accept these grouped objects and update its internal references to use values.*, options.*, and actions.* so the page composition stays stable as filters grow, while keeping existing symbol names (FiltersSidebar, filters, BOUNTY_TYPES, STATUSES, setSearchQuery, toggleType, toggleOrg, setRewardRange, setStatusFilter, clearFilters) for easy location.
🤖 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 56-60: The toolbar is receiving a misleading totalCount from
filters.filteredBounties during load/error; change the prop so BountyToolbar
only gets a numeric total when the query is successful and the count is
known—i.e., compute totalCount as filters.filteredBounties.length only when
filters indicates success (e.g., !filters.isLoading && !filters.isError /
filters.status === 'success'), otherwise pass undefined/null (or omit the prop)
so BountyToolbar can render an indeterminate/loading state; update the call site
where BountyToolbar is rendered (the BountyToolbar totalCount prop) and ensure
you reference filters.filteredBounties, filters.isLoading/filters.isError (or
filters.status) and BountyToolbar to locate the change.
---
Nitpick comments:
In `@app/bounty/page.tsx`:
- Around line 37-53: The FiltersSidebar prop list is too wide; refactor page.tsx
and FiltersSidebar to accept three grouped props (e.g., values, options,
actions) instead of many individual props: move current state read-only items
(filters.searchQuery, filters.selectedTypes, filters.selectedOrgs,
filters.rewardRange, filters.statusFilter, filters.organizations) into a single
values object, move static choices (BOUNTY_TYPES, STATUSES) into an options
object, and move callbacks (filters.setSearchQuery, filters.toggleType,
filters.toggleOrg, filters.setRewardRange, filters.setStatusFilter,
filters.clearFilters, filters.hasActiveFilters) into an actions object; update
the FiltersSidebar component signature and its prop types to accept these
grouped objects and update its internal references to use values.*, options.*,
and actions.* so the page composition stays stable as filters grow, while
keeping existing symbol names (FiltersSidebar, filters, BOUNTY_TYPES, STATUSES,
setSearchQuery, toggleType, toggleOrg, setRewardRange, setStatusFilter,
clearFilters) for easy location.
🪄 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: d6a5e396-2e35-4905-9dee-f39a2281b0de
📒 Files selected for processing (6)
app/bounty/page.tsxcomponents/bounty/bounty-grid.tsxcomponents/bounty/bounty-toolbar.tsxcomponents/bounty/filters-sidebar.tsxhooks/__tests__/use-bounty-filters.test.tshooks/use-bounty-filters.ts
✅ Files skipped from review due to trivial changes (2)
- components/bounty/bounty-toolbar.tsx
- hooks/tests/use-bounty-filters.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- components/bounty/bounty-grid.tsx
- hooks/use-bounty-filters.ts
- components/bounty/filters-sidebar.tsx
| <BountyToolbar | ||
| totalCount={filters.filteredBounties.length} | ||
| sortOption={filters.sortOption} | ||
| onSortChange={filters.setSortOption} | ||
| /> |
There was a problem hiding this comment.
Don't render a fake 0 results state while the query is loading or failed.
BountyToolbar only knows about totalCount, so during the initial fetch and on errors it can only render the empty-array fallback from filters.filteredBounties. That makes the page look like a real empty result set when the data is actually unknown.
Suggested fix
<main className="flex-1 min-w-0">
- <BountyToolbar
- totalCount={filters.filteredBounties.length}
- sortOption={filters.sortOption}
- onSortChange={filters.setSortOption}
- />
+ {!isLoading && !isError && (
+ <BountyToolbar
+ totalCount={filters.filteredBounties.length}
+ sortOption={filters.sortOption}
+ onSortChange={filters.setSortOption}
+ />
+ )}
<BountyGrid🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/bounty/page.tsx` around lines 56 - 60, The toolbar is receiving a
misleading totalCount from filters.filteredBounties during load/error; change
the prop so BountyToolbar only gets a numeric total when the query is successful
and the count is known—i.e., compute totalCount as
filters.filteredBounties.length only when filters indicates success (e.g.,
!filters.isLoading && !filters.isError / filters.status === 'success'),
otherwise pass undefined/null (or omit the prop) so BountyToolbar can render an
indeterminate/loading state; update the call site where BountyToolbar is
rendered (the BountyToolbar totalCount prop) and ensure you reference
filters.filteredBounties, filters.isLoading/filters.isError (or filters.status)
and BountyToolbar to locate the change.
Summary: Transformed the monolithic
BountiesPageinto a clean, orchestrated architecture using the Atomic Design pattern.Key Changes:
useBountyFilters.FiltersSidebar,BountyToolbar, andBountyGridto separate concerns.OPENvsopen).Result:
page.tsxreduced from 411 to 72 lines. All UI behavior remains identical but with improved maintainability.closes #127
Summary by CodeRabbit
New Features
Improvements