Skip to content

feat: Implement API-driven bounty fetching and display with dedicated loading, empty, and error UI components. - #34

Merged
0xdevcollins merged 2 commits into
boundlessfi:mainfrom
Dprof-in-tech:feat-integrate-fetch-bounties
Jan 25, 2026
Merged

feat: Implement API-driven bounty fetching and display with dedicated loading, empty, and error UI components.#34
0xdevcollins merged 2 commits into
boundlessfi:mainfrom
Dprof-in-tech:feat-integrate-fetch-bounties

Conversation

@Dprof-in-tech

@Dprof-in-tech Dprof-in-tech commented Jan 25, 2026

Copy link
Copy Markdown
Contributor

This pull request introduces a new API route for fetching bounties with filter support and refactors the bounty listing UI to use a new data-fetching hook and reusable UI states. It adds skeleton loading, error, and empty state components for a better user experience and centralizes bounty list rendering into a new BountyList component.

API and Data Fetching Improvements:

  • Added a new API route at app/api/bounties/route.ts that returns a filtered list of bounties based on query parameters such as status, type, difficulty, and search terms.
  • Refactored app/bounty/page.tsx to use the new useBounties hook for data fetching instead of importing mock data directly. [1] [2]

UI/UX Enhancements:

  • Implemented skeleton loading (BountyCardSkeleton, BountyListSkeleton), error (BountyError), and empty (BountyEmpty) state components for the bounty list, improving feedback during loading, error, or empty results. [1] [2] [3]
  • Updated the bounty page to display these new UI states during loading or error scenarios.

Component Abstraction:

  • Created a new BountyList component that encapsulates data fetching, filtering, and all UI states, allowing for easier reuse and cleaner page code.

SCREEN RECORDING

Screen.Recording.2026-01-25.at.15.24.02.mov

closes #33

Summary by CodeRabbit

  • New Features
    • Bounty list now supports filtering by status, type, difficulty, and keyword search with server-side filtering and pagination.
    • Improved loading experience with card skeletons and a skeleton grid.
    • Clear empty-state messaging with an option to clear filters.
    • Enhanced error UI with retry capability and integrated retry flow in the bounties view.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 25, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds a GET API route for fetching and filtering bounties and refactors the bounty UI to use a useBounties query hook with new loading, error, empty, and list/skeleton components; page and components now render based on query states and support retry/clear filters.

Changes

Cohort / File(s) Summary
API Route
app/api/bounties/route.ts
New Next.js API GET handler that reads status, type, difficulty, search params, fetches mock bounties, applies sequential filters (status/type/difficulty/search), simulates 500ms latency, and returns paginated JSON.
Bounty Page Update
app/bounty/page.tsx
Switched from local mock to useBounties hook; uses isLoading/isError/data from hook; renders BountyListSkeleton when loading and BountyError with retry on error.
Skeleton Loading Components
components/bounty/bounty-card-skeleton.tsx
New client components: BountyCardSkeleton (card placeholder) and BountyListSkeleton (grid of skeletons, configurable count).
Error UI
components/bounty/bounty-error.tsx
New client component BountyError rendering an error message and optional retry button wired to provided callback.
Empty State UI
components/bounty/bounty-empty.tsx
New client component BountyEmpty showing no-results messaging, accepts hasFilters and onClearFilters to optionally render a "Clear all filters" action.
Bounty List Component
components/bounty/bounty-list.tsx
New client orchestration component using useBounties hook; conditionally renders skeleton, error, empty, or a grid of BountyCard items; supports params, hasFilters, onClearFilters, and onBountyClick.

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant BountyList as BountyList (component)
    participant Hook as useBounties (query hook)
    participant API as /api/bounties (Next API)
    participant Data as MockDataSource

    Browser->>BountyList: mount / user interaction
    BountyList->>Hook: call useBounties(params)
    Hook->>API: GET /api/bounties?status=...&type=...&difficulty=...&search=...
    API->>Data: fetch all bounties
    Data-->>API: returns list
    API->>API: apply filters (status, type, difficulty, search)
    API-->>Hook: 200 JSON { data, pagination } (after ~500ms)
    Hook-->>BountyList: resolves data / isLoading false / isError false
    BountyList-->>Browser: render grid of BountyCard (or skeleton / error / empty)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Hops of code on a moonlit night,
Filters tune, and the data takes flight,
Skeletons wait as requests go ping,
Errors mend, empty states sing,
A rabbit cheers for the bounty spring! 🎉

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements most core requirements from issue #33 but falls short on complete scope: missing updates to app/page.tsx, no modifications to bounty-card.tsx for real data, no toast notifications for errors, and no refetch-on-focus configuration. Update app/page.tsx to use useBounties hook, modify bounty-card.tsx to consume real data, add toast notifications for errors, and configure refetch on window focus and reconnect in the useBounties hook.
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 (3 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 changes: implementing API-driven bounty fetching with dedicated UI components for loading, error, and empty states.
Out of Scope Changes check ✅ Passed All changes are within scope of issue #33 requirements; new components and API route directly support the objective of integrating API-driven bounty fetching with proper UI states.

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

✨ Finishing touches
  • 📝 Generate docstrings

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@app/bounty/page.tsx`:
- Around line 30-31: The temporary array reference for allBounties (from
useBounties()) causes dependent useMemo hooks to re-run; replace the simple
assignment with a stable memoized value by computing allBounties via useMemo and
depending on the fetched payload (e.g., data?.data) so the reference only
changes when the actual data changes—update the declaration of allBounties to
use useMemo(() => data?.data ?? [], [data?.data]) so the downstream useMemo
hooks that reference allBounties are stable.
🧹 Nitpick comments (3)
app/api/bounties/route.ts (2)

7-8: Remove or conditionally apply the artificial delay for production.

The 500ms delay is useful for development to test loading states, but it will degrade user experience in production. Consider making it conditional based on environment.

♻️ Suggested fix
-    // Simulate network delay
-    await new Promise(resolve => setTimeout(resolve, 500));
+    // Simulate network delay in development only
+    if (process.env.NODE_ENV === 'development') {
+        await new Promise(resolve => setTimeout(resolve, 500));
+    }

15-22: Consider validating filter parameter values.

The filter parameters (status, type, difficulty) accept any string value without validation. While the current implementation will simply return no matches for invalid values, explicit validation could provide better error feedback and prevent unexpected behavior.

♻️ Example validation approach
const VALID_STATUSES = ['open', 'claimed', 'closed'];
const VALID_TYPES = ['feature', 'bug', 'documentation', 'refactor', 'other'];
const VALID_DIFFICULTIES = ['beginner', 'intermediate', 'advanced'];

const status = searchParams.get('status');
if (status && !VALID_STATUSES.includes(status)) {
    return NextResponse.json({ error: 'Invalid status parameter' }, { status: 400 });
}
app/bounty/page.tsx (1)

374-388: Consider reusing the BountyEmpty component for consistency.

The empty state UI here duplicates the BountyEmpty component that was introduced in this PR. Using the shared component would ensure visual consistency and reduce duplication.

♻️ Suggested refactor

First, add the import at the top of the file:

import { BountyEmpty } from "@/components/bounty/bounty-empty"

Then replace the inline empty state:

                         ) : (
-                            <div className="flex flex-col items-center justify-center py-24 text-center border border-dashed border-gray-800 rounded-2xl bg-background-card/30">
-                                <div className="size-16 rounded-full bg-gray-800/50 flex items-center justify-center mb-4">
-                                    <Search className="size-8 text-gray-600" />
-                                </div>
-                                <h3 className="text-xl font-bold mb-2 text-gray-200">No bounties found</h3>
-                                <p className="text-gray-400 max-w-md mx-auto mb-6">
-                                    We couldn&apos;t find any bounties matching your current filters.
-                                    Try adjusting your search terms or filters.
-                                </p>
-                                <Button onClick={clearFilters} variant="outline" className="border-gray-700 hover:bg-gray-800">
-                                    Clear all filters
-                                </Button>
-                            </div>
+                            <BountyEmpty hasFilters={true} onClearFilters={clearFilters} />
                         )}

Comment thread app/bounty/page.tsx Outdated
@0xdevcollins
0xdevcollins merged commit 886846e into boundlessfi:main Jan 25, 2026
2 checks passed
This was referenced Jan 25, 2026
0xDeon pushed a commit to 0xDeon/bounties that referenced this pull request Jan 26, 2026
…etch-bounties

feat: Implement API-driven bounty fetching and display with dedicated loading, empty, and error UI components.
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.

Integrate Queries with Bounty Components

2 participants