Skip to content

fix: Buy Now button navigation and NFT detail page (#292)#426

Open
Chithra582 wants to merge 2 commits into
IndieHub25:mainfrom
Chithra582:fix/buy-now-button
Open

fix: Buy Now button navigation and NFT detail page (#292)#426
Chithra582 wants to merge 2 commits into
IndieHub25:mainfrom
Chithra582:fix/buy-now-button

Conversation

@Chithra582
Copy link
Copy Markdown

@Chithra582 Chithra582 commented Feb 24, 2026

fixes #292

I have changed the code of the nft-gallery file in the component. I have fixed the buy now button in the nft.

Summary by CodeRabbit

  • New Features

    • Added "Buy Now" button for direct navigation to NFT detail pages
    • Implemented best sellers filtering and sorting
    • Added data loading functionality
  • UI/UX Improvements

    • Enhanced responsive grid layout
    • Simplified price display format
    • Improved card footer spacing

@vercel
Copy link
Copy Markdown

vercel Bot commented Feb 24, 2026

Someone is attempting to deploy a commit to the Drago's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added bug Something isn't working fix size/M labels Feb 24, 2026
@github-actions github-actions Bot requested a review from Drago-03 February 24, 2026 08:26
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 24, 2026

📝 Walkthrough

Walkthrough

Updated NFTGallery component to add client-side routing capability and data loading. Introduced best sellers filtering by sales count, updated UI layout, and replaced "View Details" button with functional "Buy Now" button that navigates to individual NFT detail pages.

Changes

Cohort / File(s) Summary
NFT Gallery Enhancement
components/nft-gallery.tsx
Added client-side routing with Next.js useRouter for navigation to NFT detail pages. Implemented data loading state and fetchStories function. Added bestSellers filtering logic (salesCount > 0). Updated CardFooter layout spacing and removed "Price:" label prefix. Replaced "View Details" button with "Buy Now" button that triggers navigation to /nft/{id}.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

ECWoC26, ECWoC26-L2

Suggested reviewers

  • Drago-03

Poem

🐰 A button that clicks with delight,
Now routes to detail pages bright!
Best sellers sorted with care,
NFT riches to share,
The gallery hops left and right! 🛍️

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is minimal and does not follow the required template. It lacks issue reference formatting, summary of changes, type of change selection, technical checklist, testing evidence, and mandatory acknowledgements. Update the description to follow the template structure: add proper issue reference, detailed summary of changes with context, select applicable change types, complete relevant technical checklist items, and include all mandatory acknowledgements.
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
Title check ✅ Passed The title accurately reflects the main change: fixing the Buy Now button navigation and implementing NFT detail page routing.
Linked Issues check ✅ Passed The PR successfully implements the core requirements: Buy Now button now navigates to /nft/:id route, NFT detail page routing is implemented via router.push, and data loading/filtering for best sellers is in place.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing the Buy Now button and NFT detail navigation. Minor layout updates (CardFooter gap, price display, responsive grid) are directly related to the button fix objective.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
Contributor

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
components/nft-gallery.tsx (2)

39-49: ⚠️ Potential issue | 🟠 Major

Missing response.ok guard will silently corrupt state on API errors.

response.json() is called unconditionally regardless of the HTTP status. If /api/stories returns a non-2xx response, the parsed error body is passed directly to setStories(data), which will cause bestSellers filtering to silently fail (or worse, crash the renderer) since the payload won't conform to Story[].

Additionally, when the catch branch fires, isLoading is set to false and the component renders the empty "No best-selling NFTs" message — giving the user no indication that something went wrong.

🛡️ Proposed fix
  const fetchStories = async () => {
+   setIsLoading(true);
    try {
      const response = await fetch('/api/stories');
+     if (!response.ok) {
+       throw new Error(`Failed to fetch stories: ${response.status}`);
+     }
      const data = await response.json();
      setStories(data);
    } catch (error) {
      console.error('Failed to fetch stories:', error);
+     // Surface the failure so the user doesn't see a misleading empty state
+     // e.g. setError(true) and render an error message in JSX
    } finally {
      setIsLoading(false);
    }
  };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/nft-gallery.tsx` around lines 39 - 49, fetchStories currently
calls response.json() unconditionally and may pass an error payload into
setStories; update fetchStories to first check response.ok and, if false, parse
the error body (or read text) and throw a new Error containing that message so
you never call setStories with a non-Story payload; add a small error state
(e.g., fetchError via useState) and in the catch setFetchError(error) instead of
relying on the empty list UI, then keep setIsLoading(false) in finally;
reference: fetchStories, setStories, setIsLoading and add setFetchError/useState
for the UI to show an error.

83-90: ⚠️ Potential issue | 🟡 Minor

Next.js <Image> will throw on an empty or invalid imageUrl; add an onError fallback.

story.imageUrl comes directly from the API with no guard. An empty string or missing URL will cause the <Image> component to error, breaking the card render entirely.

🛡️ Proposed fix
+ const [imgSrc, setImgSrc] = useState(story.imageUrl);
  <Image
-   src={story.imageUrl}
+   src={imgSrc || '/placeholder-nft.png'}
    alt={story.title}
    fill
    className="object-cover rounded-md"
    sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 25vw"
    loading="lazy"
+   onError={() => setImgSrc('/placeholder-nft.png')}
  />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/nft-gallery.tsx` around lines 83 - 90, The Image can throw when
story.imageUrl is empty/invalid; update the JSX in components/nft-gallery.tsx to
compute a safe src (e.g., const imageSrc = story.imageUrl ||
'/images/placeholder.png') and pass that to the Image component (still using
story.title for alt), and add an onError handler on the Image (or its wrapper)
that replaces the failed image with a fallback (set e.currentTarget.src to the
placeholder or toggle a local "usePlaceholder" state) so rendering never breaks
when story.imageUrl is missing or invalid.
🧹 Nitpick comments (1)
components/nft-gallery.tsx (1)

35-49: fetchStories is referenced in useEffect before it is declared; missing exhaustive-deps entry.

fetchStories is a const arrow function defined at line 39, but useEffect at line 36 captures it before its declaration in the function body. While this doesn't cause a runtime TDZ error (React's effect fires after render, when the binding is resolved), it will trigger an react-hooks/exhaustive-deps ESLint warning, and is a subtle ordering hazard if the code is refactored.

The idiomatic fix is to move fetchStories inside useEffect, or — if the function needs to remain callable elsewhere — wrap it in useCallback and include it in the deps array.

♻️ Proposed fix (move into useEffect)
- useEffect(() => {
-   fetchStories();
- }, []);

- const fetchStories = async () => {
-   try {
-     const response = await fetch('/api/stories');
-     if (!response.ok) throw new Error(`${response.status}`);
-     const data = await response.json();
-     setStories(data);
-   } catch (error) {
-     console.error('Failed to fetch stories:', error);
-   } finally {
-     setIsLoading(false);
-   }
- };

+ useEffect(() => {
+   const fetchStories = async () => {
+     try {
+       const response = await fetch('/api/stories');
+       if (!response.ok) throw new Error(`${response.status}`);
+       const data = await response.json();
+       setStories(data);
+     } catch (error) {
+       console.error('Failed to fetch stories:', error);
+     } finally {
+       setIsLoading(false);
+     }
+   };
+   fetchStories();
+ }, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/nft-gallery.tsx` around lines 35 - 49, The useEffect references
the fetchStories arrow function before its declaration which triggers
react-hooks/exhaustive-deps; fix by either moving the fetchStories
implementation into the useEffect callback (so useEffect directly performs the
async fetch and calls setStories/setIsLoading) or convert fetchStories into a
memoized function using useCallback (exporting const fetchStories =
useCallback(async () => { ... }, [...deps]) and then include fetchStories in the
useEffect deps array). Update references to setStories and setIsLoading
accordingly and ensure the deps array is correct for useCallback if you choose
that approach.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/nft-gallery.tsx`:
- Around line 101-107: Create an API proxy route (app/api/stories/route.ts) that
fetches from the backend /api/v1/stories, extracts response.data (the stories
array), maps each item to convert _id → id (since server/routes/stories.js uses
.lean()), and returns that stories array as the JSON response; update the
frontend code to call fetch('/api/stories') and pass the returned array into
setStories(storiesArray) (not the wrapper object), ensure the frontend Story
interface and uses of story.id (e.g., router.push(`/nft/${story.id}`) and the
Button onClick) expect id:string, and add a detail page component
(app/nft/[id]/page.tsx) that fetches the single story via the proxy (or a new
proxy endpoint app/api/stories/[id]/route.ts) and renders the NFT detail so
clicking "Buy Now" does not 404.

---

Outside diff comments:
In `@components/nft-gallery.tsx`:
- Around line 39-49: fetchStories currently calls response.json()
unconditionally and may pass an error payload into setStories; update
fetchStories to first check response.ok and, if false, parse the error body (or
read text) and throw a new Error containing that message so you never call
setStories with a non-Story payload; add a small error state (e.g., fetchError
via useState) and in the catch setFetchError(error) instead of relying on the
empty list UI, then keep setIsLoading(false) in finally; reference:
fetchStories, setStories, setIsLoading and add setFetchError/useState for the UI
to show an error.
- Around line 83-90: The Image can throw when story.imageUrl is empty/invalid;
update the JSX in components/nft-gallery.tsx to compute a safe src (e.g., const
imageSrc = story.imageUrl || '/images/placeholder.png') and pass that to the
Image component (still using story.title for alt), and add an onError handler on
the Image (or its wrapper) that replaces the failed image with a fallback (set
e.currentTarget.src to the placeholder or toggle a local "usePlaceholder" state)
so rendering never breaks when story.imageUrl is missing or invalid.

---

Nitpick comments:
In `@components/nft-gallery.tsx`:
- Around line 35-49: The useEffect references the fetchStories arrow function
before its declaration which triggers react-hooks/exhaustive-deps; fix by either
moving the fetchStories implementation into the useEffect callback (so useEffect
directly performs the async fetch and calls setStories/setIsLoading) or convert
fetchStories into a memoized function using useCallback (exporting const
fetchStories = useCallback(async () => { ... }, [...deps]) and then include
fetchStories in the useEffect deps array). Update references to setStories and
setIsLoading accordingly and ensure the deps array is correct for useCallback if
you choose that approach.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 330ac4c and 1559e0e.

📒 Files selected for processing (1)
  • components/nft-gallery.tsx

Comment on lines +101 to 107
<Button
variant="default"
size="sm"
onClick={() => router.push(`/nft/${story.id}`)}
>
Buy Now
</Button>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Feb 24, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# 1. Search for NFT detail page route
fd -type f \( -name "page.tsx" -o -name "page.ts" \) | xargs grep -l "nft\|NFT" 2>/dev/null || echo "No NFT pages found with grep"
fd -type f -path "*/nft/*" \( -name "page.tsx" -o -name "page.ts" \) 2>/dev/null || echo "No nft directory route found"

# 2. Check the actual nft.js server file for _id handling
if [ -f "server/routes/nft.js" ]; then
  echo "=== server/routes/nft.js content ===" 
  cat -n "server/routes/nft.js"
else
  echo "server/routes/nft.js not found"
  fd -type f -name "nft.js" 
fi

# 3. Search for any API transformation or id aliasing
rg -n "id|_id" --type ts --type tsx -A 2 -B 2 | head -100

Repository: IndieHub25/GroqTales

Length of output: 30238


🏁 Script executed:

# 4. List all app/page routes to understand structure
fd -type f -path "*/app/*" \( -name "page.tsx" -o -name "page.ts" \) 2>/dev/null | head -20

# 5. Search for any nft-related routes or files
fd -type f -name "*nft*" 2>/dev/null

Repository: IndieHub25/GroqTales

Length of output: 46


🏁 Script executed:

# 6. Check nft-gallery.tsx to understand what data structure is being used
if [ -f "components/nft-gallery.tsx" ]; then
  echo "=== nft-gallery.tsx ===" 
  cat -n "components/nft-gallery.tsx" | head -150
else
  fd -type f -name "nft-gallery.tsx"
fi

Repository: IndieHub25/GroqTales

Length of output: 4603


🏁 Script executed:

# Search for /api/stories endpoint
fd -type f -name "route.ts" -o -name "route.js" | xargs grep -l "stories" 2>/dev/null || echo "Searching in app directory..."
find . -path "*/app/*" -type f \( -name "route.ts" -o -name "route.js" \) | head -20

# Also search for any stories API handler
rg -l "stories" --type ts --type js | grep -E "(api|route)" | head -10

Repository: IndieHub25/GroqTales

Length of output: 1172


🏁 Script executed:

# List the app directory structure to understand routing
find . -path "*/app/*" -type d | head -20

Repository: IndieHub25/GroqTales

Length of output: 510


🏁 Script executed:

# Search more broadly for stories endpoint
rg "api/stories|/stories" --type ts --type js -A 3 | head -50

Repository: IndieHub25/GroqTales

Length of output: 2332


🏁 Script executed:

# Search for /api/stories route in Next.js app directory
find ./app/api -name "route.ts" -o -name "route.js" | xargs grep -l "stories" 2>/dev/null || echo "No stories route found in app/api"

# Check if there's a stories folder in app/api
ls -la ./app/api/ | grep stories || echo "No stories directory"
fd "stories" ./app/api 2>/dev/null || echo "stories not in app/api"

# Check next.config for rewrites/proxying
if [ -f "next.config.js" ] || [ -f "next.config.ts" ] || [ -f "next.config.mjs" ]; then
  echo "=== next.config files ===" 
  cat next.config.* 2>/dev/null | head -100
fi

Repository: IndieHub25/GroqTales

Length of output: 2469


🏁 Script executed:

# Search for where /api/stories is actually handled
rg "GET.*stories|api/stories" -A 5 | head -60

Repository: IndieHub25/GroqTales

Length of output: 3032


🏁 Script executed:

# Check the stories.js server file to see response structure
cat -n server/routes/stories.js | head -150

Repository: IndieHub25/GroqTales

Length of output: 5091


🏁 Script executed:

# Search for middleware or response transformation
rg "api/stories|middleware" -A 3 -B 3 | head -80

Repository: IndieHub25/GroqTales

Length of output: 50377


🏁 Script executed:

# Look for middleware files
fd "middleware" -type f 2>/dev/null

Repository: IndieHub25/GroqTales

Length of output: 46


🏁 Script executed:

# Check if there's a proxy setup or fetch wrapper
rg "fetch.*stories|axios.*stories" -A 5 | head -60

Repository: IndieHub25/GroqTales

Length of output: 2178


🏁 Script executed:

# Verify the exact response being received - check if /api/stories is actually a Next.js route
fd -type f \( -name "page.tsx" -o -name "layout.tsx" \) -path "*/app/*" | xargs grep -l "fetch.*stories" 2>/dev/null | head -5

Repository: IndieHub25/GroqTales

Length of output: 234


🏁 Script executed:

# Check all Next.js route handlers in app/api directory
find ./app/api -name "route.ts" -o -name "route.js" 2>/dev/null | xargs -I {} sh -c 'echo "=== {} ===" && head -30 {}'

Repository: IndieHub25/GroqTales

Length of output: 18824


🏁 Script executed:

# Specifically look for stories endpoint - check if /api/stories exists anywhere
rg "export.*GET|export.*POST.*stories" -A 10 ./app/api 2>/dev/null | head -80

Repository: IndieHub25/GroqTales

Length of output: 5043


🏁 Script executed:

# Search for any adapter or data transformation for stories response
rg "stories.*data|data.*pagination" ./app -B 2 -A 2 | head -60

Repository: IndieHub25/GroqTales

Length of output: 1322


Three critical blocking issues prevent NFT purchase flow:

  1. Missing /api/stories endpoint — The component calls fetch('/api/stories') but no Next.js route handler exists at /app/api/stories/. The backend has /api/v1/stories (Express), not accessible to the frontend via /api/stories. This will fail immediately with a 404.

  2. Response structure mismatch — Even if the endpoint existed, the backend returns { data: stories[], pagination: {...} } but the code does setStories(data) expecting a stories array. This would set the entire response object as state instead of the array.

  3. _id vs id field mismatch — The backend query uses .lean() (line 86 in server/routes/stories.js), which strips Mongoose virtuals. MongoDB documents have _id, not id. The frontend expects story.id (line 19 in nft-gallery.tsx interface), so router.push('/nft/' + story.id) will navigate to /nft/undefined.

  4. Missing /nft/[id] detail page — No app/nft/[id]/page.tsx exists. Even if the above issues were fixed, clicking "Buy Now" would 404.

Current code (lines 101-107)
                <Button
                  variant="default"
                  size="sm"
                  onClick={() => router.push(`/nft/${story.id}`)}
                >
                  Buy Now
                </Button>

Implement: (1) create /app/api/stories/route.ts that proxies to backend and transforms _idid in response, (2) ensure response is the stories array directly, (3) add /app/nft/[id]/page.tsx, and (4) verify data shape matches frontend interface.

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

In `@components/nft-gallery.tsx` around lines 101 - 107, Create an API proxy route
(app/api/stories/route.ts) that fetches from the backend /api/v1/stories,
extracts response.data (the stories array), maps each item to convert _id → id
(since server/routes/stories.js uses .lean()), and returns that stories array as
the JSON response; update the frontend code to call fetch('/api/stories') and
pass the returned array into setStories(storiesArray) (not the wrapper object),
ensure the frontend Story interface and uses of story.id (e.g.,
router.push(`/nft/${story.id}`) and the Button onClick) expect id:string, and
add a detail page component (app/nft/[id]/page.tsx) that fetches the single
story via the proxy (or a new proxy endpoint app/api/stories/[id]/route.ts) and
renders the NFT detail so clicking "Buy Now" does not 404.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@Chithra582 update with missing endpoints and correct mismatched fields

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@vercel
Copy link
Copy Markdown

vercel Bot commented Feb 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
groq-tales Ready Ready Preview, Comment Feb 24, 2026 1:34pm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working fix size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Basic] Fix “Buy Now” Button

2 participants