Use Categories#51
Conversation
WalkthroughMigrates the storefront from taxon/taxonomies to category-based APIs and routes: adds category data wrappers, removes taxonomies module and page, renames props/paths from taxon→category, fetches root categories in StorefrontLayout and passes them to Header, and initializes StoreProvider with markets passed from root layout. Changes
Sequence Diagram(s)sequenceDiagram
participant Req as Browser Request
participant Layout as StorefrontLayout (server)
participant CategoriesAPI as getCategories (server)
participant RootHeader as Header (server-render)
participant RootLayout as Root Layout (server)
participant MarketsAPI as getMarkets (server)
participant StoreProv as StoreProvider (client/server)
participant Router as next/router
Req->>Layout: request /c/{permalink}
Layout->>CategoriesAPI: listCategories(depth_eq:0)
CategoriesAPI-->>Layout: rootCategories
Layout->>RootHeader: render Header with rootCategories
Req->>RootLayout: page-level render
RootLayout->>MarketsAPI: getMarkets()
MarketsAPI-->>RootLayout: initialMarkets
RootLayout->>StoreProv: initialize(initialMarkets, initialCountry/Locale)
StoreProv->>StoreProv: compute initialCountries/initialResolved
alt needs redirect
StoreProv->>Router: router.replace(newPath) and set cookies
end
RootHeader-->>Req: rendered header with category links
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 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 (3)
src/app/[country]/[locale]/(storefront)/c/[...permalink]/page.tsx (1)
17-83: AddgenerateMetadatafor SEO.As per coding guidelines, pages with route params should use
generateMetadatato dynamically generate SEO metadata. This page fetches category data that could populate title, description, and OpenGraph tags.📝 Suggested implementation
import type { Metadata } from "next"; export async function generateMetadata({ params }: CategoryPageProps): Promise<Metadata> { const { permalink } = await params; const fullPermalink = permalink.join("/"); try { const category = await getCategory(fullPermalink); return { title: category?.name, description: category?.description, }; } catch { return {}; } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`[country]/[locale]/(storefront)/c/[...permalink]/page.tsx around lines 17 - 83, Add a server-side generateMetadata function to produce dynamic SEO metadata for this paramized page: implement export async function generateMetadata({ params }: CategoryPageProps): Promise<Metadata> that builds fullPermalink from params.permalink, calls getCategory(fullPermalink) (handle errors with try/catch) and returns title: category?.name and description: category?.description (plus optional openGraph fields if desired); keep it separate from the CategoryPage default export and import Metadata type from "next".src/components/layout/Footer.tsx (1)
22-36: Both "All Products" and "Categories" links point to the same URL.Lines 23 and 31 both link to
/products, which may confuse users expecting distinct destinations. Consider either:
- Differentiating the destinations (e.g.,
/productsvs/categoriesor/products?view=categories)- Removing one of the duplicate links
- Adding a query parameter to distinguish the intent (e.g.,
/products?filter=categories)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/layout/Footer.tsx` around lines 22 - 36, The two Link elements in Footer.tsx render "All Products" and "Categories" but both use href="/products"; update the href on the Link that renders the "Categories" text to a distinct destination (for example change the Link with children "Categories" to href="/categories" or href="/products?filter=categories") so the Link component with inner text "Categories" no longer points to the same URL as the Link component with inner text "All Products".src/app/[country]/[locale]/(storefront)/page.tsx (1)
29-35: Both buttons link to the same destination.The "Shop Now" and "Browse Categories" buttons now both navigate to
${basePath}/products. While this may be intentional after removing the taxonomies page, having two adjacent buttons with identical destinations could confuse users. Consider whether "Browse Categories" should link to a different view (e.g.,/products?view=categoriesor a future categories landing page) or if one button should be removed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`[country]/[locale]/(storefront)/page.tsx around lines 29 - 35, The two adjacent Button components (Button size="lg" asChild and Button variant="outline" size="lg" asChild) both use Link href={`${basePath}/products`}, which duplicates destinations; update the second Button (the one labeled "Browse Categories") to point to a distinct destination (for example change its Link href to `${basePath}/products?view=categories` or to the planned categories landing route) or remove the duplicate button entirely so users are not confused.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/`[country]/[locale]/(storefront)/c/[...permalink]/page.tsx:
- Around line 60-68: Replace the plain <a> used inside category.children.map
with Next.js' Link to enable client-side navigation: import Link from
"next/link" at the top of the file and swap the <a key={child.id}
href={`${basePath}/c/${child.permalink}`} className="...">...</a> for a <Link
href={`${basePath}/c/${child.permalink}`} key={child.id} className="...">
{child.name} </Link> (retain the same className and content); ensure the unique
key remains on the Link element so React list rendering is preserved.
---
Nitpick comments:
In `@src/app/`[country]/[locale]/(storefront)/c/[...permalink]/page.tsx:
- Around line 17-83: Add a server-side generateMetadata function to produce
dynamic SEO metadata for this paramized page: implement export async function
generateMetadata({ params }: CategoryPageProps): Promise<Metadata> that builds
fullPermalink from params.permalink, calls getCategory(fullPermalink) (handle
errors with try/catch) and returns title: category?.name and description:
category?.description (plus optional openGraph fields if desired); keep it
separate from the CategoryPage default export and import Metadata type from
"next".
In `@src/app/`[country]/[locale]/(storefront)/page.tsx:
- Around line 29-35: The two adjacent Button components (Button size="lg"
asChild and Button variant="outline" size="lg" asChild) both use Link
href={`${basePath}/products`}, which duplicates destinations; update the second
Button (the one labeled "Browse Categories") to point to a distinct destination
(for example change its Link href to `${basePath}/products?view=categories` or
to the planned categories landing route) or remove the duplicate button entirely
so users are not confused.
In `@src/components/layout/Footer.tsx`:
- Around line 22-36: The two Link elements in Footer.tsx render "All Products"
and "Categories" but both use href="/products"; update the href on the Link that
renders the "Categories" text to a distinct destination (for example change the
Link with children "Categories" to href="/categories" or
href="/products?filter=categories") so the Link component with inner text
"Categories" no longer points to the same URL as the Link component with inner
text "All Products".
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4cdfc6e2-37d6-468c-99cf-dde73fc75d46
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (19)
package.jsonsrc/app/[country]/[locale]/(storefront)/c/[...permalink]/CategoryProductsContent.tsxsrc/app/[country]/[locale]/(storefront)/c/[...permalink]/loading.tsxsrc/app/[country]/[locale]/(storefront)/c/[...permalink]/page.tsxsrc/app/[country]/[locale]/(storefront)/layout.tsxsrc/app/[country]/[locale]/(storefront)/page.tsxsrc/app/[country]/[locale]/(storefront)/taxonomies/page.tsxsrc/app/[country]/[locale]/layout.tsxsrc/components/layout/Footer.tsxsrc/components/layout/Header.tsxsrc/components/navigation/Breadcrumbs.tsxsrc/components/products/ProductCarousel.tsxsrc/contexts/StoreContext.tsxsrc/hooks/useCarouselProducts.tssrc/lib/analytics/gtm.tssrc/lib/data/categories.tssrc/lib/data/index.tssrc/lib/data/products.tssrc/lib/data/taxonomies.ts
💤 Files with no reviewable changes (3)
- src/app/[country]/[locale]/(storefront)/taxonomies/page.tsx
- src/lib/data/products.ts
- src/lib/data/taxonomies.ts
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 (1)
src/app/[country]/[locale]/(storefront)/c/[...permalink]/page.tsx (1)
18-84:⚠️ Potential issue | 🟠 MajorAdd
generateMetadatafunction to generate category-specific SEO metadata.This page has dynamic route parameters and fetches category data with name and description, but does not export
generateMetadata. Category pages lack SEO/social preview metadata and fall back to generic defaults. The category's name, description, and image should be used to generate appropriatetitle,description, and OG tags for search engines and social sharing.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`[country]/[locale]/(storefront)/c/[...permalink]/page.tsx around lines 18 - 84, Add an exported async generateMetadata function that mirrors CategoryPage's params handling (use the same params -> country, locale, permalink -> fullPermalink and basePath), call getCategory(fullPermalink, { expand: ["ancestors","children"] }) to fetch the category, and return a metadata object using category.name for title, category.description for description, and include openGraph (og:title, og:description, og:image using category.image_url), twitter card info, and a canonical URL built from basePath + "/c/" + fullPermalink; handle fetch errors or missing category by returning sensible fallback metadata (or call notFound()) to avoid breaking the page.
🧹 Nitpick comments (1)
src/app/[country]/[locale]/(storefront)/c/[...permalink]/page.tsx (1)
18-25: Tighten the TypeScript surface here.
let category;weakens the main render path to an implicit loose type, andCategoryPagestill has no explicit return type. Please givecategorya concrete type and declare the component return type explicitly. As per coding guidelines:**/*.ts{,x}: Use strict TypeScript type checking; define explicit return types for functions and avoid 'any' type.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`[country]/[locale]/(storefront)/c/[...permalink]/page.tsx around lines 18 - 25, The function and local variable lack explicit TypeScript types; update the component signature and the category variable: change CategoryPage to declare an explicit async return type (e.g., export default async function CategoryPage({ params }: CategoryPageProps): Promise<JSX.Element>) and give category a concrete type (for example let category: Category | null = null or const category: Category = await getCategory(...) if getCategory always returns a Category). Import the appropriate Category (or equivalent) type used by getCategory and adjust null/undefined handling in the render path to satisfy the stricter type (use runtime checks or throw if missing).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/`[country]/[locale]/(storefront)/c/[...permalink]/page.tsx:
- Around line 24-30: The catch currently maps every getCategory(fullPermalink,
...) failure to notFound(), causing transient/network errors to show a 404;
change the catch to inspect the thrown error (e.g., if error is a Response with
error.status === 404 or an API error object with status === 404) and call
notFound() only in that case, otherwise rethrow the error so upstream/error
boundaries can handle retries/logging; update the catch around getCategory to
perform this conditional check and rethrow non-404 errors.
---
Outside diff comments:
In `@src/app/`[country]/[locale]/(storefront)/c/[...permalink]/page.tsx:
- Around line 18-84: Add an exported async generateMetadata function that
mirrors CategoryPage's params handling (use the same params -> country, locale,
permalink -> fullPermalink and basePath), call getCategory(fullPermalink, {
expand: ["ancestors","children"] }) to fetch the category, and return a metadata
object using category.name for title, category.description for description, and
include openGraph (og:title, og:description, og:image using category.image_url),
twitter card info, and a canonical URL built from basePath + "/c/" +
fullPermalink; handle fetch errors or missing category by returning sensible
fallback metadata (or call notFound()) to avoid breaking the page.
---
Nitpick comments:
In `@src/app/`[country]/[locale]/(storefront)/c/[...permalink]/page.tsx:
- Around line 18-25: The function and local variable lack explicit TypeScript
types; update the component signature and the category variable: change
CategoryPage to declare an explicit async return type (e.g., export default
async function CategoryPage({ params }: CategoryPageProps):
Promise<JSX.Element>) and give category a concrete type (for example let
category: Category | null = null or const category: Category = await
getCategory(...) if getCategory always returns a Category). Import the
appropriate Category (or equivalent) type used by getCategory and adjust
null/undefined handling in the render path to satisfy the stricter type (use
runtime checks or throw if missing).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5773efa4-34a8-4ced-85da-a64f086f4d12
📒 Files selected for processing (1)
src/app/[country]/[locale]/(storefront)/c/[...permalink]/page.tsx
| try { | ||
| taxon = await getTaxon(fullPermalink, { | ||
| category = await getCategory(fullPermalink, { | ||
| expand: ["ancestors", "children"], | ||
| }); | ||
| } catch (error) { | ||
| console.error("Failed to fetch taxon:", error); | ||
| console.error("Failed to fetch category:", error); | ||
| notFound(); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, read the complete page.tsx file to see full context
cat -n src/app/[country]/[locale]/\(storefront\)/c/\[..\.permalink\]/page.tsxRepository: spree/storefront
Length of output: 3169
🏁 Script executed:
# Search for the getCategory function definition
rg -A 20 "export\s+(?:async\s+)?function\s+getCategory|const\s+getCategory\s*=" --type ts --type tsxRepository: spree/storefront
Length of output: 87
🏁 Script executed:
# Check for error types or status codes used in error handling
rg "notFound\(\)" -B 5 -A 2 --type ts --type tsx | head -50Repository: spree/storefront
Length of output: 87
🏁 Script executed:
# Search for getCategory in the categories data file
find . -name "categories.ts" -o -name "categories.tsx" | head -5Repository: spree/storefront
Length of output: 88
🏁 Script executed:
# Try to find getCategory with rg without file type specification
rg "getCategory" -A 15 | head -80Repository: spree/storefront
Length of output: 6515
🏁 Script executed:
# Look for Spree API error handling patterns in the codebase
rg "catch.*error" -A 3 -B 1 | grep -A 3 "notFound" | head -50Repository: spree/storefront
Length of output: 331
🏁 Script executed:
# Read the full categories.ts file
cat -n src/lib/data/categories.tsRepository: spree/storefront
Length of output: 833
🏁 Script executed:
# Search for other error handling patterns with status codes
rg "status|statusCode|404|notFound" -B 3 -A 3 | head -100Repository: spree/storefront
Length of output: 5989
🏁 Script executed:
# Look for HTTP error handling or Spree SDK imports to understand error structure
rg "@spree|HttpError|ApiError" -B 2 -A 2 | head -80Repository: spree/storefront
Length of output: 3785
🏁 Script executed:
# Search for how Spree SDK errors are handled in the codebase
rg "error\s*as\s*\w+|error\s*:\s*\w+|error\.\w+" -B 2 -A 2 | head -60Repository: spree/storefront
Length of output: 3120
🏁 Script executed:
# Look for any HTTP status checking in similar data fetching patterns
rg "response|status|code" -B 3 -A 3 | grep -A 3 -B 3 "getCategory\|getProduct\|get[A-Z]" | head -80Repository: spree/storefront
Length of output: 4863
🏁 Script executed:
# Check if there are examples of conditional error handling in the codebase
rg "throw|Error|catch" -B 2 -A 3 | grep -A 5 "if\|status\|code" | head -80Repository: spree/storefront
Length of output: 4912
🏁 Script executed:
# Check if Spree SDK provides error types or status codes
rg "error\.status|error\.code|error\.response|HttpError|ApiError" -B 2 -A 2 | head -80Repository: spree/storefront
Length of output: 42
🏁 Script executed:
# Look at the withFallback utility to see how errors are handled there
rg -A 15 "export.*withFallback|function withFallback" src/lib/data/utils.tsRepository: spree/storefront
Length of output: 257
🏁 Script executed:
# Check other data fetching pages to see error handling patterns
fd "page.tsx" src/app | xargs rg -l "catch.*error" | head -5Repository: spree/storefront
Length of output: 125
🏁 Script executed:
# Search for Spree SDK documentation or error handling in package.json or README
cat package.json | grep -A 5 -B 5 "@spree"Repository: spree/storefront
Length of output: 412
🏁 Script executed:
# Look for any error handling examples with status or type checking in other files
rg "catch.*error|error.*status|error.*message" -B 3 -A 5 src/lib/data/ | head -100Repository: spree/storefront
Length of output: 1115
🏁 Script executed:
# Check if there's a products page that handles errors differently for comparison
cat -n src/app/\[country\]/\[locale\]/\(storefront\)/products/\[slug\]/page.tsx 2>/dev/null | head -50Repository: spree/storefront
Length of output: 593
🏁 Script executed:
# Check if there are any tests that show how getCategory errors are expected to be handled
find . -name "*.test.ts" -o -name "*.test.tsx" | xargs rg "getCategory" -B 3 -A 5 2>/dev/null | head -80Repository: spree/storefront
Length of output: 42
🏁 Script executed:
# Look for any middleware or error boundary that handles API errors
rg "error\.status|response\.status|404|notFound" -B 3 -A 3 | grep -v node_modules | head -100Repository: spree/storefront
Length of output: 3066
🏁 Script executed:
# Check if there's documentation on Spree error handling in comments or docs
find . -name "*.md" -o -name "*.tsx" -o -name "*.ts" | xargs rg "Spree.*error|SDK.*error|notFound.*404" 2>/dev/null | head -50Repository: spree/storefront
Length of output: 42
🏁 Script executed:
# Check the actual Spree SDK behavior - look for how getCategory behaves with non-existent categories
rg "response\.data|response\.success|null|undefined" src/lib/data/categories.ts -B 3 -A 3Repository: spree/storefront
Length of output: 42
🏁 Script executed:
# Look at Spree/Next documentation or types to understand error structure
find . -name "*.d.ts" | xargs grep -l "getCategory" 2>/dev/null | head -3Repository: spree/storefront
Length of output: 42
🏁 Script executed:
# Check if getCategory might return null/undefined instead of throwing
rg "getCategory" -B 5 -A 15 src/ --type ts --type tsx | grep -A 10 "return\|null\|undefined" | head -50Repository: spree/storefront
Length of output: 87
🏁 Script executed:
# Check how the Spree SDK might indicate "not found" - look for response structure
rg "\.data|\.success|\.errors" src/lib/data/ -B 2 -A 2 | head -60Repository: spree/storefront
Length of output: 643
🏁 Script executed:
# Search for any network/API error handling patterns to understand error types
rg "throw|Error\(" src/lib/data/ -B 3 -A 3 | head -80Repository: spree/storefront
Length of output: 4899
🏁 Script executed:
# Look at the full ProductDetailsWrapper to understand how it handles getProduct errors
cat -n src/app/\[country\]/\[locale\]/\(storefront\)/products/\[slug\]/ProductDetailsWrapper.tsxRepository: spree/storefront
Length of output: 3124
🏁 Script executed:
# Check if there's any distinction in how Spree SDK errors are handled elsewhere
rg "getProduct|listProducts" -A 8 src/lib/data/products.tsRepository: spree/storefront
Length of output: 571
🏁 Script executed:
# Look for any error inspection patterns with instanceof or type checking
rg "instanceof|error\.type|error\.code|error\.name" src/ -B 2 -A 2 | head -80Repository: spree/storefront
Length of output: 670
🏁 Script executed:
# Check what happens when getCategory/getProduct return null or empty
rg "category|product" src/app/\[country\]/\[locale\]/\(storefront\)/c/\[..\.permalink\]/page.tsx -B 2 -A 2 | head -40Repository: spree/storefront
Length of output: 1255
🏁 Script executed:
# Check if there's any way to distinguish error types in Spree SDK
rg "HttpError|NotFoundError|error\.response|error\.status" -B 2 -A 2Repository: spree/storefront
Length of output: 42
🏁 Script executed:
# Look for any comments or documentation about error handling
rg "error|exception|404" src/lib/data/categories.ts -B 3 -A 3Repository: spree/storefront
Length of output: 42
🏁 Script executed:
# Check if there are any integration tests showing expected behavior
find . -name "*.test.ts" -o -name "*.spec.ts" | xargs cat 2>/dev/null | grep -A 20 "getCategory\|notFound" | head -80Repository: spree/storefront
Length of output: 42
Distinguish real "not found" from transient errors in the catch block.
This catch block turns every getCategory() failure into notFound(), so a transient API/network error will render as a false 404 page. Only map actual "category not found" (404) responses to notFound() and rethrow other errors.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/app/`[country]/[locale]/(storefront)/c/[...permalink]/page.tsx around
lines 24 - 30, The catch currently maps every getCategory(fullPermalink, ...)
failure to notFound(), causing transient/network errors to show a 404; change
the catch to inspect the thrown error (e.g., if error is a Response with
error.status === 404 or an API error object with status === 404) and call
notFound() only in that case, otherwise rethrow the error so upstream/error
boundaries can handle retries/logging; update the catch around getCategory to
perform this conditional check and rethrow non-404 errors.
Summary by CodeRabbit
Refactor
Bug Fixes
Chores