fix: Buy Now button navigation and NFT detail page (#292)#426
fix: Buy Now button navigation and NFT detail page (#292)#426Chithra582 wants to merge 2 commits into
Conversation
|
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. |
📝 WalkthroughWalkthroughUpdated 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
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 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
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 | 🟠 MajorMissing
response.okguard will silently corrupt state on API errors.
response.json()is called unconditionally regardless of the HTTP status. If/api/storiesreturns a non-2xx response, the parsed error body is passed directly tosetStories(data), which will causebestSellersfiltering to silently fail (or worse, crash the renderer) since the payload won't conform toStory[].Additionally, when the
catchbranch fires,isLoadingis set tofalseand 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 | 🟡 MinorNext.js
<Image>will throw on an empty or invalidimageUrl; add anonErrorfallback.
story.imageUrlcomes 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:fetchStoriesis referenced inuseEffectbefore it is declared; missing exhaustive-deps entry.
fetchStoriesis aconstarrow function defined at line 39, butuseEffectat 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 anreact-hooks/exhaustive-depsESLint warning, and is a subtle ordering hazard if the code is refactored.The idiomatic fix is to move
fetchStoriesinsideuseEffect, or — if the function needs to remain callable elsewhere — wrap it inuseCallbackand 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.
| <Button | ||
| variant="default" | ||
| size="sm" | ||
| onClick={() => router.push(`/nft/${story.id}`)} | ||
| > | ||
| Buy Now | ||
| </Button> |
There was a problem hiding this comment.
🧩 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 -100Repository: 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/nullRepository: 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"
fiRepository: 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 -10Repository: IndieHub25/GroqTales
Length of output: 1172
🏁 Script executed:
# List the app directory structure to understand routing
find . -path "*/app/*" -type d | head -20Repository: 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 -50Repository: 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
fiRepository: IndieHub25/GroqTales
Length of output: 2469
🏁 Script executed:
# Search for where /api/stories is actually handled
rg "GET.*stories|api/stories" -A 5 | head -60Repository: 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 -150Repository: IndieHub25/GroqTales
Length of output: 5091
🏁 Script executed:
# Search for middleware or response transformation
rg "api/stories|middleware" -A 3 -B 3 | head -80Repository: IndieHub25/GroqTales
Length of output: 50377
🏁 Script executed:
# Look for middleware files
fd "middleware" -type f 2>/dev/nullRepository: 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 -60Repository: 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 -5Repository: 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 -80Repository: 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 -60Repository: IndieHub25/GroqTales
Length of output: 1322
Three critical blocking issues prevent NFT purchase flow:
-
Missing
/api/storiesendpoint — The component callsfetch('/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. -
Response structure mismatch — Even if the endpoint existed, the backend returns
{ data: stories[], pagination: {...} }but the code doessetStories(data)expecting a stories array. This would set the entire response object as state instead of the array. -
_idvsidfield mismatch — The backend query uses.lean()(line 86 inserver/routes/stories.js), which strips Mongoose virtuals. MongoDB documents have_id, notid. The frontend expectsstory.id(line 19 in nft-gallery.tsx interface), sorouter.push('/nft/' + story.id)will navigate to/nft/undefined. -
Missing
/nft/[id]detail page — Noapp/nft/[id]/page.tsxexists. 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 _id → id 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.
There was a problem hiding this comment.
@Chithra582 update with missing endpoints and correct mismatched fields
There was a problem hiding this comment.
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!
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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
UI/UX Improvements