Skip to content

Conversation

@ShakthiW
Copy link
Member

@ShakthiW ShakthiW commented Nov 19, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added loading states to password reset and gift redemption flows for smoother user experience.
    • Improved error messaging throughout checkout and gift processes with detailed feedback.
  • Bug Fixes

    • Fixed apostrophe display issues in various text elements across the app.
    • Enhanced error handling and validation across API endpoints.
  • Performance

    • Optimized component rendering with improved memoization.
    • Upgraded navigation to client-side routing for faster page transitions.

@coderabbitai
Copy link

coderabbitai bot commented Nov 19, 2025

Walkthrough

This PR modernizes API route handlers by updating to Promise-based dynamic route parameters (Next.js 15 pattern), introduces structured type definitions for checkout requests and API responses, hardens error handling with safe JSON parsing, improves time value normalization, enhances gift flow processing, refactors category/experience data fetching with fallback strategies, replaces anchor tags with Next.js Link components, and improves overall type safety throughout the codebase.

Changes

Cohort / File(s) Summary
API Route Parameter Modernization
src/app/api/reviews/[id]/helpful/route.ts, src/app/api/reviews/[id]/unhelpful/route.ts, src/app/api/reviews/[id]/route.ts
Updated handler signatures from destructured params to context-based context: { params: Promise<{id: string}> }, added awaiting of params, updated fetch URLs, and introduced parseJsonSafely helper for robust JSON parsing with fallback error handling.
Checkout Route Hardening
src/app/api/checkout/route.ts
Added structured request types (CheckoutCustomer, CheckoutItem, CheckoutGiftDetails, CheckoutRequestBody), introduced isApiErrorResponse and normaliseTimeValue helpers, improved gift flow logic with itemId-based selection, enhanced error parsing for API responses, and added cookie merging for gift redemption.
User API Routes Type Safety
src/app/api/users/forgot-password/route.ts, src/app/api/users/login/route.ts, src/app/api/users/profile/route.ts, src/app/api/users/reset-password/route.ts
Introduced ApiResponseData interfaces for typed response handling, added safe JSON parsing with fallback handling, improved error message extraction with defaults, enhanced Set-Cookie header propagation, and standardized error responses to return data or empty object on success.
Navigation Component Updates
src/app/auth/forgot-password/page.tsx, src/app/auth/signup/page.tsx, src/components/forgot-password-form.tsx, src/components/reset-password-form.tsx
Replaced anchor tags with Next.js Link components for client-side routing, added Suspense boundary in reset-password page, escaped apostrophes in text content.
Cart & Checkout UI Refinements
src/app/cart/page.tsx, src/app/cart/error.tsx, src/app/checkout/error.tsx, src/app/checkout/page.tsx
Escaped apostrophes in JSX text ("haven't", "won't"), enhanced error message display to append error.message, removed duplicate state declarations, improved error parsing on checkout submission.
Category & Experience Data Fetching
src/app/experiences/category/[slug]/page.tsx, src/app/experiences/subcategory/[mainCategory]/[subCategorySlug]/page.tsx
Introduced typed category system (CategoryType, CategoryEntity, SubCategory), added fetchSubcategories and getExperienceCount with CMS API fallback, added category metadata and type guards, replaced hardcoded data with metadata-driven approach.
Experience Details & Listings Optimization
src/app/experiences/page.tsx, src/components/listings/ExperienceDetails.tsx, src/components/categories/CategoryPage.tsx, src/components/landing/DiscoverSection.tsx
Removed id prop from ListingCard component, added slug field to Experience interface, introduced useCallback optimization in ExperienceDetails, improved type safety for location handling, replaced index-based keys with slug-based identification.
Gift Redemption Page Refactoring
src/app/gift/redeem/page.tsx
Introduced Suspense wrapper, extracted logic into GiftRedeemContent component, added defensive error parsing with fallback messages, improved availability data fetching with safe JSON parsing, corrected booking item construction, added error display in order summary.
Component Hook & Import Cleanup
src/components/listings/BookingForm.tsx, src/components/listings/tabs/LocationTab.tsx, src/components/listings/tabs/ReviewsTab.tsx
Updated useEffect dependency array in BookingForm, removed unused imports in LocationTab, refactored review rendering logic with explicit handlers and authentication guards in ReviewsTab.
UI Component Type Refactoring
src/components/ui/textarea.tsx
Converted TextareaProps from interface to type alias, adjusted formatting/semicolons.
Type Definitions & Context
src/types/index.ts, src/contexts/CartContext.tsx
Updated ApiExperience.reviews from any[] to Review[], introduced PersistedCartItem and PersistedCartState for localStorage deserialization with date string handling, improved persisted cart typing.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant OldHandler as Old API Handler
    participant NewHandler as New API Handler
    participant Backend

    Note over OldHandler: Handler({ params: { id } })
    Client->>OldHandler: PATCH /api/reviews/[id]/helpful
    OldHandler->>OldHandler: Use params.id directly
    OldHandler->>Backend: Fetch with id
    Backend-->>OldHandler: Response
    OldHandler->>OldHandler: response.json() (unsafe)
    OldHandler-->>Client: Result

    Note over NewHandler: Handler(Request, context: { params: Promise<{id}> })
    Client->>NewHandler: PATCH /api/reviews/[id]/helpful
    NewHandler->>NewHandler: await context.params → { id }
    NewHandler->>Backend: Fetch with id
    Backend-->>NewHandler: Response
    NewHandler->>NewHandler: parseJsonSafely(response)
    Note right of NewHandler: Safe extraction with fallback
    NewHandler-->>Client: Result
Loading
sequenceDiagram
    participant Client
    participant CheckoutAPI as POST /api/checkout
    participant BookingBackend as Booking Service
    participant GiftBackend as Gift Service
    participant Response

    Client->>CheckoutAPI: POST with items, customer, gift details
    CheckoutAPI->>CheckoutAPI: Type check to CheckoutRequestBody
    CheckoutAPI->>CheckoutAPI: Extract giftDetails by itemId if isGift
    alt Gift found
        CheckoutAPI->>CheckoutAPI: Populate receiverEmail, giftMessage
    else Gift not found
        CheckoutAPI->>Response: Error 400
    end
    CheckoutAPI->>CheckoutAPI: Normalize times via normaliseTimeValue
    CheckoutAPI->>BookingBackend: POST booking payload
    BookingBackend-->>CheckoutAPI: Response (ok/error with message)
    alt Booking success & isGift
        CheckoutAPI->>GiftBackend: POST gift redemption with cookies
        GiftBackend-->>CheckoutAPI: Redemption response
    end
    CheckoutAPI->>Response: Final 200/error response
    Response-->>Client: Result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Areas requiring extra attention:

  • Checkout route refactoring (src/app/api/checkout/route.ts): New type system with multiple interfaces and helper functions; gift handling logic changes need verification against business requirements
  • API route signature updates (reviews, user routes): Pattern is consistent but applied across 7+ files; verify params Promise handling and error message extraction are correctly implemented everywhere
  • Category/experience data fetching (src/app/experiences/category/[slug]/page.tsx, subcategory page): Complex new type system with CMS API integration and fallback strategies; verify API calls and data shape transformations
  • Experience details optimization (src/components/listings/ExperienceDetails.tsx): useCallback dependency management and optional chaining safety; verify re-fetch behavior is correct
  • Gift redemption refactoring (src/app/gift/redeem/page.tsx): Component extraction with Suspense; verify error states and data flow through GiftRedeemContent

Possibly related PRs

  • PR #7: Directly modifies src/app/api/checkout/route.ts with structured payload types and gift handling logic that this PR builds upon
  • PR #6: Updates review API route handlers (src/app/api/reviews/*) with similar parameter signature and error handling patterns as this PR
  • PR #3: Refactors user API routes (src/app/api/users/*/route.ts) with request/response handling and JSON parsing improvements parallel to this PR's changes

Poem

🐰 Hops through types with Promise-based grace,
Gift flows now find their proper place,
Safe JSON parsing catches the fall,
Link components route it all,
Type safety reigns—no more fright!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'Fix/linting errors' is too vague and generic; it does not clearly convey the primary or most significant changes in this comprehensive pull request. Use a more specific title that summarizes the main change, such as 'Improve type safety and error handling across API routes and components' or focus on the most impactful change if there is one primary objective.
Docstring Coverage ⚠️ Warning Docstring coverage is 18.52% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/linting_errors

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • JIRA integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


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

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

🧹 Nitpick comments (20)
src/components/forgot-password-form.tsx (1)

116-121: Replace anchor tags with Next.js Link component for internal navigation.

For client-side navigation in a Next.js 15 application, use the Link component instead of raw <a> tags to enable client-side transitions and avoid full-page reloads.

Apply this diff to replace the anchor tags:

 import { cn } from "@/lib/utils";
 import { Button } from "@/components/ui/button";
+import Link from "next/link";
 import {
   Card,
   CardContent,

Then update the navigation links:

-                <a
-                  href="/auth/login"
+                <Link
+                  href="/auth/login"
                   className="ml-auto text-sm underline-offset-4 hover:underline"
                 >
                   Remember your password?
-                </a>
+                </Link>
-              <a href="/auth/signup" className="underline underline-offset-4">
+              <Link href="/auth/signup" className="underline underline-offset-4">
                 Sign up
-              </a>
+              </Link>

Also applies to: 137-139

src/app/auth/reset-password/page.tsx (1)

21-23: Correctly addresses the Next.js 15 requirement for useSearchParams().

The Suspense boundary is properly placed around ResetPasswordForm, which uses the useSearchParams() hook. This is required by Next.js 15 App Router to handle dynamic rendering and prevents hydration errors.

The fallback UI is functional and appropriate for a linting fix. Optionally, you could enhance it to match the Card-based design system used in the form itself for visual consistency, but since the fallback is rarely shown (searchParams are typically available immediately), the current implementation is acceptable.

If you'd like to match the design system more closely, consider this enhancement:

-        <Suspense fallback={<div className="text-center text-sm text-muted-foreground">Loading reset form...</div>}>
+        <Suspense fallback={
+          <Card>
+            <CardContent className="pt-6">
+              <div className="text-center text-sm text-muted-foreground">
+                Loading reset form...
+              </div>
+            </CardContent>
+          </Card>
+        }>
           <ResetPasswordForm />
         </Suspense>

Note: This would require importing Card and CardContent from your UI components.

src/app/gift/redeem/page.tsx (3)

275-281: Clarify the Suspense fallback message.

The Suspense boundary is correctly used here (required for useSearchParams in Next.js 15), but the fallback message "Loading gift details..." is misleading. This boundary handles useSearchParams initialization, not the actual gift data fetching—that's managed by internal state (line 63) and displays separately (lines 180-183).

Consider a more accurate message:

-    <Suspense fallback={<div className="text-center text-sm text-muted-foreground">Loading gift details...</div>}>
+    <Suspense fallback={<div className="text-center text-sm text-muted-foreground">Loading...</div>}>

92-97: Consider simplifying the error message extraction.

The current error parsing logic is defensive but verbose. You can simplify it while maintaining type safety:

-        const errorData = await response.json().catch(() => null);
-        const errorMessage =
-          errorData && typeof errorData === "object" && "message" in errorData
-            ? (errorData as { message?: string }).message
-            : undefined;
-        throw new Error(errorMessage || "Something went wrong");
+        const errorData = await response.json().catch(() => ({}));
+        const errorMessage = 
+          typeof errorData === "object" && errorData && "message" in errorData && typeof errorData.message === "string"
+            ? errorData.message
+            : "Something went wrong";
+        throw new Error(errorMessage);

Or extract to a helper function if this pattern repeats:

function extractErrorMessage(errorData: unknown): string {
  if (typeof errorData === "object" && errorData && "message" in errorData && typeof errorData.message === "string") {
    return errorData.message;
  }
  return "Something went wrong";
}

124-130: Extract the inline type definition to improve readability.

The inline type assertion on lines 124-126 makes the code harder to read. Consider defining a proper interface:

Add this interface near the top of the file (after the redeemGiftProps interface):

interface AvailabilityResponse {
  availableTimes?: ApiTime[];
  fullyBooked?: boolean;
}

Then simplify the fetch:

-    const data = (await response.json().catch(() => null)) as
-      | { availableTimes?: ApiTime[]; fullyBooked?: boolean }
-      | null;
+    const data = (await response.json().catch(() => null)) as AvailabilityResponse | null;
src/components/reset-password-form.tsx (1)

26-30: Good type safety improvement.

The interface provides clear structure for API responses. The index signature allows flexibility for additional properties.

Consider adding runtime validation using a library like Zod to ensure the parsed JSON actually matches this interface, rather than relying solely on type assertions:

import { z } from 'zod';

const ApiResponseSchema = z.object({
  error: z.string().optional(),
  message: z.string().optional(),
}).passthrough();

type ApiResponseData = z.infer<typeof ApiResponseSchema>;

Then at line 95, use: data = ApiResponseSchema.safeParse(await response.json()).data ?? null;

src/components/listings/BookingForm.tsx (1)

82-88: useEffect dependency change may cause extra resets when callback identity changes

Including onDateChange in the dependency array fixes the lint warning and avoids stale callbacks, but it also means:

  • setTime(undefined) and onDateChange(date) now run whenever the parent passes a new onDateChange reference, even if date hasn’t changed.

If the parent passes an inline function (not wrapped in useCallback), this could unexpectedly clear the selected time on re-renders. Consider:

  • Ensuring the parent wraps onDateChange in useCallback, or
  • Moving the setTime(undefined) behind a guard that only runs when date actually changes, if this becomes a UX issue.
src/app/api/reviews/[id]/unhelpful/route.ts (1)

4-17: Safe JSON parsing and updated PATCH handler look solid; consider deduplicating helper

  • parseJsonSafely correctly:
    • Checks content-type for JSON,
    • Catches .json() failures (e.g., empty/invalid bodies),
    • Returns null when parsing isn’t safe, which is handled via data?.message ?? default.
  • Using await context.params and interpolating id into the backend URL aligns with the other review routes and keeps the handler consistent.
  • Success path NextResponse.json(data ?? {}, { status: 200 }) gives a defined payload even for 204/empty responses, which is reasonable.

Since parseJsonSafely is duplicated in the “helpful” route, consider moving it to a shared utility (e.g., @/lib/api/parseJsonSafely) to avoid drift in future edits.

Also applies to: 19-57

src/components/listings/tabs/ReviewsTab.tsx (1)

185-271: Per‑review helpful/unhelpful handling is consistent and user‑aware

  • isHelpful / isUnhelpful correctly guard against user being null and missing arrays via optional chaining and ?? false.
  • The button handlers:
    • Redirect unauthenticated users to /auth/login before doing anything,
    • Delegate to onHelpful / onUnhelpful when provided, keeping side effects at a higher level.
  • Disabling buttons when the current user has already marked helpful/unhelpful is a sensible UX choice.

If this list grows large, you could later extract the mapped block into a small <ReviewItem> component to avoid redefining handlers per item, but that’s strictly an optional cleanup.

src/app/api/reviews/[id]/helpful/route.ts (1)

4-17: Helpful route mirrors unhelpful route patterns; good consistency, minor duplication

  • parseJsonSafely and the updated PATCH signature mirror the unhelpful route, which keeps behavior and error handling aligned across both endpoints.
  • Error responses use a clear default message and surface backend data?.message when available, which should help debugging without leaking raw responses.

As with the unhelpful route, consider extracting parseJsonSafely into a shared helper to avoid divergence between these two files in the future.

Also applies to: 20-57

src/app/api/reviews/[id]/route.ts (1)

37-57: PUT/DELETE header forwarding is good; consider reusing safe JSON parsing

  • For PUT and DELETE, forwarding cookie and authorization from the incoming request improves auth consistency with the helpful/unhelpful endpoints.
  • All three handlers still use await response.json() directly. If the backend ever returns non‑JSON or an empty body on error, these calls could throw before you reach the error handling.

Given you already have parseJsonSafely in the helpful/unhelpful routes, consider:

  • Using the same helper here to guard response.json(), and
  • Deriving error messages from parsed data when available, with a sensible fallback.

This would make error handling more robust and consistent across all review routes.

Also applies to: 78-97, 70-71, 109-110

src/app/api/checkout/route.ts (1)

2-61: Typed checkout models and error handling are strong; verify error response shape and status

Positives:

  • CheckoutCustomer, CheckoutItem, and CheckoutRequestBody significantly clarify the expected payload shape.
  • normaliseTimeValue cleanly handles both string and ApiTime inputs and guarantees a string selectedTime for the downstream API.
  • Duration normalization via durationValue and the Number.isFinite guard prevents NaN from leaking into payloads.
  • isApiErrorResponse plus response.json().catch(() => null) gives a safer, more informative error message (Failed to create/redeem booking: …) when the backend returns structured errors.

Two things to double‑check:

  1. Error response property name and status:

    • On failure, the route now returns:
      return NextResponse.json({ message }, { status: 500 });
    • Other API routes in this codebase (e.g., forgot‑password/login) appear to use { error: errorMessage } and often propagate the backend status code.
    • Please verify the frontend callers for /api/checkout expect message rather than error, and whether they rely on distinguishing 4xx vs 5xx via status. If they currently read error or expect non‑500 statuses from validation failures, this change could silently break error handling.
  2. Time normalization contract with the backend:

    • normaliseTimeValue returns "" when item.time is missing. Confirm the bookings API treats empty string equivalently to “no time provided” and doesn’t require null or omission instead.

If either of these assumptions doesn’t hold, small tweaks (e.g., returning { error } and/or propagating response.status) should resolve it.

Also applies to: 63-84, 86-90, 111-116, 133-138, 148-157

src/components/landing/DiscoverSection.tsx (1)

11-20: Slug-based identity is good; key fallback can be simplified

Adding a required slug field and passing it through to ListingCard is a solid move toward slug‑based routing/identification. Given slug is non‑optional, key={exp.slug ?? index} can be simplified to key={exp.slug} — the ?? index fallback will never trigger under the current type and doesn’t help with duplicate slugs.

Also applies to: 23-103, 123-126

src/contexts/CartContext.tsx (1)

4-4: LocalStorage rehydration is well-typed; consider recomputing totals

Typing the persisted shape via PersistedCartItem/PersistedCartState and converting date strings back into Date instances before dispatching LOAD_CART makes the cart state much safer to work with.

As a follow‑up improvement, you could ignore persisted subtotal/taxes/fees/total values and recompute them from itemsWithDates using calculateCartTotals when loading, so any future changes to pricing logic don’t leave old carts with stale totals.

Also applies to: 158-175

src/components/listings/ExperienceDetails.tsx (1)

131-143: Availability parsing is resilient; optionally treat non-OK responses as failures

Catching JSON parse errors and defaulting to data?.availableTimes ?? experience.availableTimes?.times ?? [] plus Boolean(data?.fullyBooked) makes the availability check tolerant of malformed responses.

For consistency with your other fetches (which check response.ok), you might optionally treat non‑OK statuses here as failures (throw on !response.ok), letting the catch path handle them and ensuring you never depend on the shape of an error payload.

src/app/experiences/category/[slug]/page.tsx (4)

1-6: CategoryType definition and imports are fine; consider centralizing the category list

The union "activities" | "occasions" | "recipients" matches usages, but it’s repeated later in isCategoryType. You could derive CategoryType from a single CATEGORY_TYPES array to avoid drift if new categories are added.

+const CATEGORY_TYPES = ["activities", "occasions", "recipients"] as const;
+type CategoryType = (typeof CATEGORY_TYPES)[number];
-
-type CategoryType = "activities" | "occasions" | "recipients";

And then reuse CATEGORY_TYPES in isCategoryType.


31-158: Fallback subcategories and metadata are robust; consider moving to a config module

The hard‑coded fallbackSubcategories and categoryMeta give a solid UX when the API fails. The tradeoff is a large literal block here and duplicated image paths between fallback data and fallbackImage. Extracting these objects into a shared config/module would keep this page leaner and make future content tweaks easier.


160-219: Good defensive fetching; watch out for the N+1 experiences counter

getExperienceCount and fetchSubcategories are defensive (returning 0 or fallback data on any failure), which is good for resilience. However, calling getExperienceCount once per entity yields an N+1 pattern against /experiences/by-category/.... If there are many subcategories, this can become slow or brittle.

If the backend can be adjusted, a single endpoint that returns subcategories and their experience counts in one payload (or accepts multiple IDs) would be a worthwhile follow‑up to reduce round‑trips.


234-261: Rendering logic and error handling look solid

  • Using categoryMeta[categorySlug] for SectionHeader keeps copy centralized.
  • endpoint={/experiences/subcategory/${categorySlug}/${subCategory.slug}} matches the subcategory page route signature.
  • On fetch or mapping failure, you log and then notFound(), which is a reasonable fallback for a category page.

Only minor nit: "${subCategory.experienceCount}+ experiences" will read slightly odd for low counts (e.g., 1+). If you expect small counts, you might want to special‑case singular/plural in a later polish pass.

src/app/experiences/subcategory/[mainCategory]/[subCategorySlug]/page.tsx (1)

21-62: Experience → Listing mapper is well-structured; minor UX polish possible

mapExperiencesToListings cleanly centralizes transformation into the Listing shape with sensible defaults for missing image, location, duration, and price. This improves readability versus in‑line mapping.

Only optional UX tweaks:

  • For location, you currently require both city and state to show anything. If the API often has just one of them, you might consider falling back to whichever is present instead of "Location not specified".

If you want to tune the location behavior, confirm how often city vs state are populated in your CMS data, then adjust the condition accordingly.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6ad3b30 and a3e7faf.

📒 Files selected for processing (31)
  • src/app/api/checkout/route.ts (5 hunks)
  • src/app/api/reviews/[id]/helpful/route.ts (2 hunks)
  • src/app/api/reviews/[id]/route.ts (6 hunks)
  • src/app/api/reviews/[id]/unhelpful/route.ts (2 hunks)
  • src/app/api/users/forgot-password/route.ts (2 hunks)
  • src/app/api/users/login/route.ts (2 hunks)
  • src/app/api/users/profile/route.ts (3 hunks)
  • src/app/api/users/reset-password/route.ts (2 hunks)
  • src/app/auth/forgot-password/page.tsx (1 hunks)
  • src/app/auth/reset-password/page.tsx (2 hunks)
  • src/app/auth/signup/page.tsx (1 hunks)
  • src/app/cart/error.tsx (1 hunks)
  • src/app/cart/page.tsx (2 hunks)
  • src/app/checkout/error.tsx (1 hunks)
  • src/app/checkout/page.tsx (3 hunks)
  • src/app/experiences/category/[slug]/page.tsx (3 hunks)
  • src/app/experiences/page.tsx (0 hunks)
  • src/app/experiences/subcategory/[mainCategory]/[subCategorySlug]/page.tsx (4 hunks)
  • src/app/gift/redeem/page.tsx (7 hunks)
  • src/components/ListingCard.tsx (0 hunks)
  • src/components/categories/CategoryPage.tsx (0 hunks)
  • src/components/forgot-password-form.tsx (1 hunks)
  • src/components/landing/DiscoverSection.tsx (10 hunks)
  • src/components/listings/BookingForm.tsx (1 hunks)
  • src/components/listings/ExperienceDetails.tsx (9 hunks)
  • src/components/listings/tabs/LocationTab.tsx (0 hunks)
  • src/components/listings/tabs/ReviewsTab.tsx (2 hunks)
  • src/components/reset-password-form.tsx (3 hunks)
  • src/components/ui/textarea.tsx (2 hunks)
  • src/contexts/CartContext.tsx (2 hunks)
  • src/types/index.ts (1 hunks)
💤 Files with no reviewable changes (4)
  • src/components/ListingCard.tsx
  • src/app/experiences/page.tsx
  • src/components/listings/tabs/LocationTab.tsx
  • src/components/categories/CategoryPage.tsx
🧰 Additional context used
🧬 Code graph analysis (11)
src/app/auth/signup/page.tsx (1)
src/app/auth/forgot-password/page.tsx (1)
  • LoginPage (5-21)
src/app/auth/reset-password/page.tsx (1)
src/components/reset-password-form.tsx (1)
  • ResetPasswordForm (32-271)
src/app/api/reviews/[id]/unhelpful/route.ts (1)
src/app/api/reviews/[id]/helpful/route.ts (1)
  • PATCH (20-64)
src/app/gift/redeem/page.tsx (2)
src/types/index.ts (1)
  • ApiTime (381-385)
src/app/auth/reset-password/page.tsx (1)
  • Page (6-27)
src/app/auth/forgot-password/page.tsx (1)
src/app/auth/signup/page.tsx (1)
  • LoginPage (5-21)
src/app/api/reviews/[id]/helpful/route.ts (1)
src/app/api/reviews/[id]/unhelpful/route.ts (1)
  • PATCH (20-64)
src/contexts/CartContext.tsx (1)
src/types/index.ts (1)
  • CartItem (448-467)
src/app/api/checkout/route.ts (2)
src/types/index.ts (1)
  • ApiTime (381-385)
src/app/api/users/forgot-password/route.ts (1)
  • POST (10-45)
src/app/experiences/subcategory/[mainCategory]/[subCategorySlug]/page.tsx (2)
src/types/index.ts (1)
  • Listing (1-12)
src/core/config/common.settings.ts (1)
  • SETTINGS (1-3)
src/app/experiences/category/[slug]/page.tsx (1)
src/core/config/common.settings.ts (1)
  • SETTINGS (1-3)
src/components/listings/ExperienceDetails.tsx (1)
src/types/index.ts (4)
  • Review (234-249)
  • ReviewStat (251-259)
  • ApiTime (381-385)
  • ApiExperience (392-432)
🔇 Additional comments (34)
src/components/ui/textarea.tsx (1)

1-23: LGTM! Clean linting fixes.

The formatting improvements are appropriate:

  • Semicolons added for consistency
  • The interfacetype change on Line 5 is semantically correct since TextareaProps is a simple alias without additional properties
src/components/forgot-password-form.tsx (1)

77-77: Revert the apostrophe escape—it will render literally as &apos; in the UI.

In JSX text content (not HTML attributes), &apos; is not decoded and will display as the literal string "'" to users instead of an apostrophe. The original unescaped apostrophe is correct. If a linting tool flagged this, it may be misconfigured for JSX.

Apply this diff to revert the change:

-            Enter your email address and we&apos;ll send you a link to reset your
+            Enter your email address and we'll send you a link to reset your

Likely an incorrect or invalid review comment.

src/app/auth/reset-password/page.tsx (1)

1-1: LGTM! Required import for Suspense boundary.

The Suspense import is correctly added to support wrapping components that use useSearchParams(), which is required by Next.js 15 App Router to avoid hydration errors.

src/app/gift/redeem/page.tsx (2)

102-109: Excellent error handling and fallback logic improvements.

The enhanced error handling across the component is well-implemented:

  • Safe initialization of availableTimes with fallback (line 102)
  • Graceful error message extraction (lines 104-108)
  • Multi-level fallback strategy for availability fetch (lines 127-130)
  • Fallback to gift data when availability fetch fails (lines 133-136)
  • User-facing error display (lines 175-179)

This ensures the component gracefully degrades and provides helpful feedback to users.

Also applies to: 127-137, 175-179


146-161: LGTM! Booking data construction is correct.

The CartItem construction properly maps the gift data and booking selections, with appropriate field assignments:

  • Uses bookingData.price for current price
  • Links back to the original gift via redeemedBookingId: gift._id
  • Preserves oldPrice from the gift's original amount
src/components/reset-password-form.tsx (4)

24-24: LGTM!

Proper Next.js import for client-side navigation.


92-96: Excellent improvements to JSON parsing.

The changes enhance code quality:

  • More descriptive variable naming (contentType vs ct)
  • Safe JSON parsing with .catch(() => null) handles malformed responses
  • Explicit type annotations improve readability

The error handling is robust and prevents crashes from invalid JSON.


99-102: LGTM!

Robust error handling with proper fallback chain: data?.errordata?.message → default message. The optional chaining safely handles null responses.


257-259: Using Next.js Link component for client-side navigation.

The /auth/login route is correctly defined at src/app/auth/login/page.tsx. The Link component change enables client-side navigation and prefetching as intended.

src/app/auth/forgot-password/page.tsx (1)

2-2: LGTM! Client-side navigation enabled.

Replacing the anchor tag with Next.js Link component enables faster client-side navigation and follows Next.js best practices.

Also applies to: 9-16

src/app/auth/signup/page.tsx (1)

2-2: LGTM! Consistent navigation pattern.

This change mirrors the navigation improvement in the forgot-password page, ensuring consistent client-side routing across authentication flows.

Also applies to: 9-16

src/app/api/users/forgot-password/route.ts (3)

25-29: LGTM! Safe JSON parsing implemented.

The content-type check and safe JSON parsing with .catch(() => null) properly handles non-JSON responses and malformed JSON.


32-37: LGTM! Improved error handling.

Error message extraction from the response data with a clear fallback message improves error reporting.


38-38: LGTM! Proper error typing.

Using error: unknown in the catch block follows TypeScript best practices for error handling.

src/app/api/users/profile/route.ts (2)

53-64: LGTM! Clean error handling in GET.

The use of parseJsonSafely and proper error message extraction with fallback improves code quality.


106-117: LGTM! Consistent error handling in PUT.

The PUT handler follows the same robust pattern as GET, ensuring consistent error handling across HTTP methods.

src/app/api/users/reset-password/route.ts (1)

32-40: LGTM! Consistent error handling and response pattern.

The error handling and response structure align well with the patterns established in other route handlers.

src/app/api/users/login/route.ts (1)

36-54: I need to gather more information about Node.js version requirements for Next.js 15.2.1 and when getSetCookie() was introduced.

I need to verify when getSetCookie() was specifically added to Node.js to confirm it's available in Node.js 18.18.0+.

Based on my verification, I've found important information about the code's approach to handling cookies. Let me provide the rewritten review comment:

The fallback for multiple Set-Cookie headers may not capture all cookies.

Next.js 15 requires Node.js 18.18.0 minimum, and Node.js 18.18.2 supports the getSetCookie() method. The optional chaining approach is correct as a safeguard. However, there's a subtle issue with the fallback: getSetCookie() returns an array containing all Set-Cookie header values, which wasn't possible prior to its implementation, but using .get('set-cookie') only returns one of the set-cookie headers when multiple exist in a response.

The current fallback will miss additional cookies if getSetCookie() is unavailable. For full compatibility, consider using response.headers.raw()?.['set-cookie'] as an additional fallback on older runtimes, or document that multiple cookies are only guaranteed when getSetCookie() is available.

src/app/cart/page.tsx (1)

48-49: Escaped apostrophes in UI copy are correct and safe

Using &apos; in the empty-cart and order-summary messages is valid JSX/HTML, avoids quoting issues, and doesn’t change behavior. Looks good.

Also applies to: 231-232

src/components/listings/tabs/ReviewsTab.tsx (1)

68-74: Auth‑gate for “Add a Review” dialog is clear and minimal

Redirecting unauthenticated users to /auth/login and returning early before opening the dialog keeps the UX straightforward and prevents unauthenticated submissions. Implementation is concise and readable.

src/app/api/reviews/[id]/route.ts (1)

5-15: Context‑based params and GET caching behavior look correct

Switching GET to context: { params: Promise<{ id: string }> } and awaiting context.params is consistent with the other review routes, and adding cache: "no-store" ensures review details aren’t cached unexpectedly. No issues spotted with the URL construction.

Also applies to: 10-15

src/types/index.ts (1)

392-432: Stronger typing for ApiExperience.reviews looks good

Switching reviews to Review[] aligns this API surface with the shared Review model and removes any-style looseness. This should help catch mismatches at compile time without changing runtime behavior.

src/app/checkout/page.tsx (3)

43-79: Gift details state wiring looks consistent

Using a Record<string, GiftDetails> keyed by item.id ties the gift inputs, review display, and the gifts payload in handleSubmit together cleanly. This should scale fine to multiple gift items without extra complexity.


95-140: Improved checkout error handling is solid

Clearing the previous error, safely attempting to parse a JSON error payload, and falling back to a generic message if there’s no usable message field makes the submit flow much more robust. Normalizing unknown errors to a string before storing in error also avoids leaking raw objects into the UI. The only behavioral note is that non‑conforming error payloads will show the generic “Something went wrong”/“One or more bookings failed.” message, which seems acceptable.


265-270: Escaped apostrophe in label is correct

Switching to Recipient&apos;s Email (Optional) is the right way to handle the apostrophe in JSX/HTML text; no issues here.

src/components/listings/ExperienceDetails.tsx (3)

3-4: Review/data fetch refactor with useCallback looks correct

Memoizing fetchReviews and fetchData and driving them via effects keyed on experienceId (and sortOption for reviews) gives predictable re-fetch behavior without obvious dependency issues. Pagination and stats updates remain consistent, and availableTimes is initialized from either initialData or the fetched experience as expected.

Also applies to: 46-78, 80-104, 105-116


176-204: Error handling for review mutations is appropriately defensive

Using error: unknown in the catch clauses for add/helpful/unhelpful review actions and logging a clear context-specific message keeps these paths type-safe without impacting user flows. Since failures here aren’t critical to the primary booking experience, logging-only behavior is reasonable.

Also applies to: 206-242


262-263: Location formatter now correctly typed

Changing formatLocation to accept ApiExperience["location"] instead of an untyped value keeps it aligned with the API model and removes an any-like hole in the type system. Implementation itself is straightforward and fine.

src/app/experiences/category/[slug]/page.tsx (2)

14-20: CategoryEntity shape looks consistent with downstream usage

The interface aligns with how you consume entities (name, slug, description?, image?). No issues from a typing or usage standpoint.


221-232: Type guard + slug validation are clean

Defining isCategoryType and early‑rejecting invalid slugs with notFound() keeps the route surface explicit and narrows slug to CategoryType before use. No issues here.

src/app/experiences/subcategory/[mainCategory]/[subCategorySlug]/page.tsx (4)

1-19: Imports, PageProps, and SubcategoryEntity are coherent

The imports and PageProps signature align with your usage (await params). SubcategoryEntity matches how you populate subcategoryId/name/description/image. Nothing problematic here.


98-125: Experiences fetch + empty state handling look correct

The experiences fetch against /experiences/by-category/${mainCategory}/${subcategoryId} with a guarded response.ok check and a safe default to [] is sound. The empty array check and rendering a CategoryPage with results={0} and listings={[]} give a clear, non-error empty state, and the non-empty path via mapExperiencesToListings is straightforward.


137-139: Top-level error handling is appropriate

Catching unexpected errors, logging "Error pre-fetching subcategory data:", and then using notFound() is a reasonable fail-safe for this route.


64-96: Backend implementation is external; manual verification needed

The /experiences/by-category endpoint is called at line 102, but the backend service (CMS_API at http://localhost:3000) is not part of this repository. The code pattern—defaulting subcategoryId to the slug, then overwriting with the CMS _id when available, with slug as fallback—appears intentional, and the same pattern is used elsewhere (e.g., category page).

To confirm the endpoint supports both identifier formats (CMS _id and fallback subCategorySlug), check the backend API documentation or implementation to ensure it handles both parameter types correctly and doesn't silently fail for slug-based queries when ID enrichment is unavailable.

Comment on lines +4 to +8
interface ApiResponseData {
message?: string;
error?: string;
[key: string]: unknown;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated ApiResponseData interface to a shared types file.

The ApiResponseData interface is duplicated across multiple API route files (forgot-password, profile, login, reset-password). This violates the DRY principle and makes maintenance harder.

Create a shared types file (e.g., src/types/api.ts or src/lib/api-types.ts):

export interface ApiResponseData {
  message?: string;
  error?: string;
  [key: string]: unknown;
}

Then import it in each route file:

 import { NextRequest, NextResponse } from "next/server";
 import { config } from "@/lib/config";
+import { ApiResponseData } from "@/types/api";

-interface ApiResponseData {
-  message?: string;
-  error?: string;
-  [key: string]: unknown;
-}
🤖 Prompt for AI Agents
In src/app/api/users/forgot-password/route.ts around lines 4 to 8, the
ApiResponseData interface is duplicated across multiple route files; extract
this shared type into a new shared file (e.g., src/types/api.ts or
src/lib/api-types.ts) exporting the interface, remove the local definition from
this file, and replace it with an import of the exported ApiResponseData in this
file (and similarly update profile, login, reset-password route files to import
the shared type).

Comment on lines +22 to 26
let data: ApiResponseData | null = null;
const contentType = response.headers.get("content-type") || "";
if (contentType.includes("application/json")) {
data = (await response.json().catch(() => null)) as ApiResponseData | null;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Use the parseJsonSafely helper for consistency.

This inline JSON parsing duplicates the logic from profile/route.ts. Once the shared utility is created (as suggested in profile route review), replace this with:

-    let data: ApiResponseData | null = null;
-    const contentType = response.headers.get("content-type") || "";
-    if (contentType.includes("application/json")) {
-      data = (await response.json().catch(() => null)) as ApiResponseData | null;
-    }
+    const data = await parseJsonSafely(response);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/app/api/users/login/route.ts around lines 22 to 26, the code does inline
JSON parsing which duplicates logic from profile/route.ts; replace this block to
call the shared parseJsonSafely helper instead: import parseJsonSafely from its
module, call it with the response (or response.text()/headers if the helper
expects them), assign the returned value to data (typed ApiResponseData | null),
and remove the manual content-type check and .catch() usage; ensure the import
is added at the top and adjust any types so data remains ApiResponseData | null.

Comment on lines +4 to +23
interface ApiResponseData {
message?: string;
error?: string;
[key: string]: unknown;
}

const parseJsonSafely = async (
response: Response
): Promise<ApiResponseData | null> => {
const contentType = response.headers.get("content-type") || "";
if (!contentType.includes("application/json")) {
return null;
}

const result = await response.json().catch(() => null);
if (result && typeof result === "object") {
return result as ApiResponseData;
}
return null;
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Extract parseJsonSafely helper and ApiResponseData to shared utilities.

The parseJsonSafely helper is well-designed and should be reused across all API routes. Currently, forgot-password, login, and reset-password routes duplicate this logic inline.

Create a shared utilities file (e.g., src/lib/api-utils.ts):

export interface ApiResponseData {
  message?: string;
  error?: string;
  [key: string]: unknown;
}

export const parseJsonSafely = async (
  response: Response
): Promise<ApiResponseData | null> => {
  const contentType = response.headers.get("content-type") || "";
  if (!contentType.includes("application/json")) {
    return null;
  }

  const result = await response.json().catch(() => null);
  if (result && typeof result === "object") {
    return result as ApiResponseData;
  }
  return null;
};

Then import in all route files:

 import { NextRequest, NextResponse } from "next/server";
 import { config } from "@/lib/config";
+import { ApiResponseData, parseJsonSafely } from "@/lib/api-utils";

-interface ApiResponseData {
-  message?: string;
-  error?: string;
-  [key: string]: unknown;
-}
-
-const parseJsonSafely = async (
-  response: Response
-): Promise<ApiResponseData | null> => {
-  // ...
-};
🤖 Prompt for AI Agents
In src/app/api/users/profile/route.ts around lines 4 to 23, the local
ApiResponseData interface and parseJsonSafely function should be extracted to a
shared utility file; create src/lib/api-utils.ts exporting the ApiResponseData
interface and parseJsonSafely function (same implementation), then remove the
duplicated declarations from this file and replace them with an import from
'src/lib/api-utils'; also update the other routes that duplicated the logic
(forgot-password, login, reset-password and any others) to import the shared
exports instead of inlining the helper so all routes reuse the single
implementation.

Comment on lines +25 to 29
let data: ApiResponseData | null = null;
const contentType = response.headers.get("content-type") || "";
if (contentType.includes("application/json")) {
data = (await response.json().catch(() => null)) as ApiResponseData | null;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Use the parseJsonSafely helper for consistency.

This inline JSON parsing duplicates logic that should be in a shared utility. Once the shared utility is created, replace with:

-    let data: ApiResponseData | null = null;
-    const contentType = response.headers.get("content-type") || "";
-    if (contentType.includes("application/json")) {
-      data = (await response.json().catch(() => null)) as ApiResponseData | null;
-    }
+    const data = await parseJsonSafely(response);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/app/api/users/reset-password/route.ts around lines 25 to 29, the code
performs inline JSON parsing which duplicates shared logic; replace it with the
parseJsonSafely helper: import parseJsonSafely at the top of the file, call it
with the Response (or response.text()/response as the helper expects) to obtain
ApiResponseData | null, and remove the manual content-type check and
.json().catch(() => null) block so the helper handles content-type detection and
safe parsing while preserving the ApiResponseData | null type.

Comment on lines +18 to 23
<h1 className="text-2xl font-bold text-gray-900 mb-2">
Something went wrong!
</h1>
<p className="text-gray-600 mb-6">
We encountered an error while loading your cart. Please try again.
We encountered an error while loading your cart. Please try again: {error.message}
</p>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t append raw error.message in cart error UI

Showing {error.message} directly in the cart error text can leak internal implementation details to users. Prefer a generic message and log the underlying error instead (or gate detailed output to development).

🤖 Prompt for AI Agents
In src/app/cart/error.tsx around lines 18 to 23, the component currently renders
the raw error.message into the UI which may leak internal details; replace the
interpolated {error.message} with a generic user-facing sentence (e.g., "Please
try again later."), remove any direct exposure of the error string in
production, and instead log the full error for diagnostics (e.g.,
console.error(error) or send to your error-tracking service). If you want to
surface details while developing, gate the raw message behind a development
check (process.env.NODE_ENV === 'development' or next/config) so only dev builds
show error.message. Ensure the UI change removes the interpolation and keep
logging/capture of the original error elsewhere.

Comment on lines +18 to 24
<h1 className="text-2xl font-bold text-gray-900 mb-2">
Checkout Error
</h1>
<p className="text-gray-600 mb-6">
We encountered an error during checkout. Please try again or return to your cart.
We encountered an error during checkout. Please try again or return to
your cart. {error.message}
</p>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid surfacing raw error.message to end users

Interpolating error.message into the checkout error copy can expose internal details (stack fragments, backend messages) in production. Consider logging it and showing a generic, user‑friendly message instead (or only showing the raw message in development).

🤖 Prompt for AI Agents
In src/app/checkout/error.tsx around lines 18 to 24, the component is directly
interpolating error.message into UI which can leak internal details; instead log
the full error (using existing logger or console.error) and replace the
interpolated value with a generic user-facing sentence like "Please contact
support or try again" — if you need to surface the actual message during
development only, conditionally render error.message when process.env.NODE_ENV
=== 'development' (or use your app's isDev flag) while keeping production output
generic.

Comment on lines +266 to +267
<Link href="/terms-of-service">Terms of Service</Link> and{" "}
<Link href="/privacy-policy">Privacy Policy</Link>.
Copy link

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

Consistent use of Next.js Link components.

Good replacement of anchor tags for Terms of Service and Privacy Policy links. Verify that these routes exist in your application.


🏁 Script executed:

#!/bin/bash
# Description: Verify the policy routes exist in the application

# Search for terms of service page/route
echo "=== Terms of Service route ==="
fd -e tsx -e ts -e jsx -e js . | xargs rg -l "terms-of-service" | head -10

echo ""
echo "=== Privacy Policy route ==="
fd -e tsx -e ts -e jsx -e js . | xargs rg -l "privacy-policy" | head -10

Length of output: 340


🏁 Script executed:

#!/bin/bash
# Description: Verify actual route pages exist for terms-of-service and privacy-policy

echo "=== Checking for Terms of Service page ==="
fd -type f \( -name "page.tsx" -o -name "page.ts" -o -name "route.ts" \) | xargs grep -l "terms-of-service" 2>/dev/null || echo "No explicit terms-of-service page found"
fd -path "*/terms-of-service/*" -type f | head -10

echo ""
echo "=== Checking for Privacy Policy page ==="
fd -type f \( -name "page.tsx" -o -name "page.ts" -o -name "route.ts" \) | xargs grep -l "privacy-policy" 2>/dev/null || echo "No explicit privacy-policy page found"
fd -path "*/privacy-policy/*" -type f | head -10

echo ""
echo "=== Checking app directory structure ==="
fd -path "*/app/*" -type d | grep -E "(terms|privacy|policy)" | head -10

echo ""
echo "=== Checking pages directory structure (if exists) ==="
fd -path "*/pages/*" -type f | grep -E "(terms|privacy|policy)" | head -10

Length of output: 1278


🏁 Script executed:

#!/bin/bash
# Description: Verify actual route pages exist for terms-of-service and privacy-policy

echo "=== Directory structure check ==="
find . -type d -name "terms-of-service" 2>/dev/null
find . -type d -name "privacy-policy" 2>/dev/null

echo ""
echo "=== Looking for page files in app directory ==="
find . -path "*/app/terms-of-service/*" -name "page.*" 2>/dev/null
find . -path "*/app/privacy-policy/*" -name "page.*" 2>/dev/null

echo ""
echo "=== All directories under app ==="
find ./app -type d 2>/dev/null | head -20

echo ""
echo "=== Looking for any route/page files ==="
find . -name "page.tsx" -o -name "page.ts" 2>/dev/null | head -20

Length of output: 593


Routes for Terms of Service and Privacy Policy do not exist in the application.

The links at lines 266-267 point to /terms-of-service and /privacy-policy, but these routes are not present in your app directory. The links will result in 404 errors. Either create these route pages (src/app/terms-of-service/page.tsx and src/app/privacy-policy/page.tsx) or update the links to point to existing routes.

🤖 Prompt for AI Agents
In src/components/reset-password-form.tsx around lines 266-267 the Link
components point to /terms-of-service and /privacy-policy which do not exist and
will 404; fix by either adding the missing route pages at
src/app/terms-of-service/page.tsx and src/app/privacy-policy/page.tsx (implement
basic content and export default a React component) or change the hrefs to
existing routes in the app (e.g., /legal/terms or /legal/privacy) ensuring the
paths match your app directory structure and updating any navigation/constants
accordingly.

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.

2 participants