Skip to content

Bump Spree SDK and Spree Next package to 0.19.0 - #90

Merged
damianlegawiec merged 4 commits into
mainfrom
chore/sdk-0-19-0
Mar 30, 2026
Merged

Bump Spree SDK and Spree Next package to 0.19.0#90
damianlegawiec merged 4 commits into
mainfrom
chore/sdk-0-19-0

Conversation

@damianlegawiec

@damianlegawiec damianlegawiec commented Mar 29, 2026

Copy link
Copy Markdown
Member
  • rename Metafields to CustomFields
  • move Next server actions to storefront directly for better DX

Summary by CodeRabbit

  • New Features

    • Product custom fields now display on product pages.
  • Improvements

    • Checkout sidebar now updates based on cart totals/quantities for more accurate refreshes.
    • Gift card entries show redemption details only when applicable.
    • Authentication, token/cookie handling, and server-side API flows streamlined for more reliable sign-in, address, cart, and payment behavior.
    • Cache invalidation improved across cart, checkout, customer, addresses, and credit-card flows.
    • Locale-aware product/category/market requests enhanced.
  • Documentation

    • README and architecture diagram updated.
  • Chores

    • Bumped Spree packages to 0.19.x.
  • Tests

    • Test suites updated to reflect new client-based API flows.

* rename Metafields to CustomFields
* move Next server actions to storefront directly for better DX
@coderabbitai

coderabbitai Bot commented Mar 29, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6af44b97-2f6d-4721-8d84-e6f5abcc4357

📥 Commits

Reviewing files that changed from the base of the PR and between e2fb54b and 8237507.

📒 Files selected for processing (1)
  • src/lib/data/customer.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/data/customer.ts

Walkthrough

Refactors server-side data layer from individual @spree/next action wrappers to direct getClient() calls with @spree/next helpers for auth/cookies and locale resolution; bumps @spree/next / @spree/sdk; migrates product metadata from metafieldscustom_fields; updates tests to mock a client object and adds cache invalidation calls.

Changes

Cohort / File(s) Summary
Documentation & Config
README.md, package.json
README updated to describe @spree/sdk usage and @spree/next cookie/auth helpers; bumped @spree/next^0.19.0, @spree/sdk^0.19.1.
Core Data Layer (client migration)
src/lib/data/...
src/lib/data/cart.ts, src/lib/data/checkout.ts, src/lib/data/payment.ts, src/lib/data/customer.ts, src/lib/data/addresses.ts, src/lib/data/credit-cards.ts, src/lib/data/gift-cards.ts, src/lib/data/orders.ts, src/lib/data/products.ts, src/lib/data/categories.ts, src/lib/data/countries.ts, src/lib/data/markets.ts, src/lib/data/cached.ts
Replaced per-function @spree/next wrappers with getClient() calls; added withAuthRefresh() usage, token/cart cookie helpers (getAccessToken/setAccessToken/requireCartId/setCartCookies/etc.), locale options via getLocaleOptions(), and updateTag() cache invalidation. Notable signature changes: getCart(explicitCartId?: string) and getOrCreateCart(params?: CreateCartParams); getMarkets options type adjusted; many internals reshaped to pass cart id/options to SDK methods.
Products & SEO
src/components/products/ProductCustomFields.tsx, src/app/.../products/[slug]/ProductDetails.tsx, src/lib/seo.ts
Replaced ProductMetafieldsProductCustomFields, switched props from metafieldscustom_fields, updated rendering to use CustomField semantics; JSON-LD now accepts broader Product type and extracts images from media.
Auth / Cookies
src/lib/data/cookies.ts, src/lib/data/customer.ts
Authentication flows rewritten to use getClient() + @spree/next token helpers; added finalizeAuth() to persist tokens and optionally associate carts; isAuthenticated() now uses getAccessToken(). Login/register/logout/reset flows updated to set/clear tokens and invalidate cache tags.
Cart & Checkout behavior
src/lib/data/cart.ts, src/lib/data/checkout.ts, src/lib/data/payment.ts
Cart APIs now propagate cart id/token via requireCartId() / getCartOptions() and call getClient().carts.*; create/update/add/remove operations invalidates "cart" tag; checkout/payment flows use client methods and call updateTag("checkout") / updateTag("cart") on success.
UI & Components
src/components/account/GiftCardList.tsx, src/components/checkout/PaymentSection.tsx, src/app/[country]/[locale]/(checkout)/checkout/[id]/page.tsx
Gift card redeemed row now conditional and layout simplified; removed shipAddressData from useImperativeHandle deps; checkout sidebar change detection key now ${cart.id}-${cart.total}-${cart.total_quantity}.
Tests
src/lib/data/__tests__/*
src/lib/data/__tests__/cart.test.ts, .../checkout.test.ts, .../customer.test.ts, .../payment.test.ts
All tests migrated from per-function mocks to a single mocked getClient() object with nested method expectations; assertions and stub shapes updated to reflect cart-id/options and token propagation; next/cache.updateTag mocked where relevant.

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant ServerCode as Storefront Data Layer
    participant SpreeClient as getClient() / `@spree/sdk`
    participant CookieHelpers as `@spree/next` Token/Cookie Helpers
    participant Cache as next/cache

    Browser->>ServerCode: POST /login {email,password}
    ServerCode->>SpreeClient: auth.login({email,password})
    SpreeClient-->>ServerCode: {access_token, refresh_token, user}
    ServerCode->>CookieHelpers: setAccessToken(access_token)
    ServerCode->>CookieHelpers: setRefreshToken(refresh_token)
    ServerCode->>SpreeClient: carts.associate(cartId, { token, spreeToken }) (optional)
    alt association success
      SpreeClient-->>ServerCode: updatedCart
      ServerCode->>Cache: updateTag("cart")
    else association failure
      ServerCode->>CookieHelpers: clearCartCookies()
    end
    ServerCode->>Cache: updateTag("customer")
    ServerCode-->>Browser: { success, user }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped from wrappers to a direct line,
getClient hums softly, tokens tucked fine.
Custom fields bloom where metafields grew,
Cache tags refreshed, cookies set true.
A little rabbit cheers this tidy design! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title states 'Bump Spree SDK and Spree Next package to 0.19.0' but the changes are significantly broader, including README restructuring, component renames (Metafields → CustomFields), server action refactoring, and widespread API migration to direct client calls. Update the title to reflect the full scope of changes, such as 'Migrate to @spree/sdk client-based API and rename Metafields to CustomFields' or 'Refactor server actions to use @spree/sdk directly and update Spree packages to 0.19.0'.
Docstring Coverage ⚠️ Warning Docstring coverage is 22.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/sdk-0-19-0

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

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

⚠️ Outside diff range comments (1)
src/lib/data/checkout.ts (1)

90-134: ⚠️ Potential issue | 🟡 Minor

Inconsistent error handling: requireCartId() called outside try-catch.

Unlike updateOrderAddresses, updateOrderMarket, and other functions that call requireCartId() inside actionResult(), applyCode calls it at lines 91-92 before any try-catch. If requireCartId() throws (e.g., redirect or custom error when no cart exists), the exception will propagate unhandled instead of returning { success: false, error: ... }.

Consider wrapping the entire function body in a try-catch for consistency:

🛡️ Suggested fix
 export async function applyCode(cartId: string, code: string) {
+  try {
   const options = await getCartOptions();
   const id = await requireCartId();

   // Try discount code first (more common)
   try {
     const cart = await getClient().carts.discountCodes.apply(id, code, options);
     // ... rest of function
   } catch (discountError) {
     // ... existing catch logic
   }
+  } catch (error) {
+    return { success: false, error: error instanceof Error ? error.message : "Failed to apply code" } as const;
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/data/checkout.ts` around lines 90 - 134, The function applyCode
currently calls requireCartId() outside any try/catch so a thrown
redirect/custom error escapes; wrap the call (and the rest of the function body)
in a top-level try/catch or move requireCartId() inside the existing try so any
exception from requireCartId is caught and returned as { success: false, error:
errorMessage(err) }; ensure you still call getCartOptions(), getClient().carts.*
and updateTag("checkout"/"cart") as before and reference applyCode,
requireCartId, and errorMessage when locating and updating the code.
🧹 Nitpick comments (5)
src/lib/data/cached.ts (1)

2-3: Consider using absolute imports for consistency.

The coding guidelines prefer @/ alias for imports. However, relative imports within the same module (e.g., ./categories) are acceptable for co-located files.

Optional: Use absolute imports
-import { getCategory } from "./categories";
-import { getProduct } from "./products";
+import { getCategory } from "@/lib/data/categories";
+import { getProduct } from "@/lib/data/products";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/data/cached.ts` around lines 2 - 3, The imports in cached.ts use
relative paths; update them to the project's preferred absolute alias by
replacing "./categories" and "./products" with the corresponding "@/..." module
paths (e.g., reference the modules where getCategory and getProduct are exported
using the '@/...' alias) so getCategory and getProduct are imported via absolute
imports for consistency with project guidelines.
src/lib/data/cart.ts (1)

83-98: Consider using getCartOptions() for consistency.

addToCart manually constructs { spreeToken, token } by calling getCartToken() and getAccessToken() separately (lines 86-87), while updateCartItem and removeCartItem use getCartOptions(). For consistency and to reduce duplication, consider using getCartOptions() here as well.

♻️ Suggested refactor
 export async function addToCart(variantId: string, quantity: number) {
   return actionResult(async () => {
     const cart = await getOrCreateCart();
-    const spreeToken = await getCartToken();
-    const token = await getAccessToken();
+    const options = await getCartOptions();

     const updatedCart = await getClient().carts.items.create(
       cart.id,
       { variant_id: variantId, quantity },
-      { spreeToken, token },
+      options,
     );

     updateTag("cart");
     return { cart: updatedCart };
   }, "Failed to add item to cart");
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/data/cart.ts` around lines 83 - 98, The addToCart function currently
calls getCartToken() and getAccessToken() directly and builds { spreeToken,
token } for getClient().carts.items.create; replace those separate calls with a
single call to getCartOptions() and pass its return value as the options
argument to getClient().carts.items.create (remove the explicit
getCartToken/getAccessToken calls), keeping the rest of addToCart (including
updateTag("cart") and returning { cart: updatedCart }) intact so it matches
updateCartItem/removeCartItem usage.
src/lib/data/__tests__/cart.test.ts (1)

180-199: Test coverage reduced for error scenarios.

The previous test suite included error-handling tests for clearCart and associateCartWithUser (including non-Error throw fallback cases). These are now removed, leaving only success-path coverage.

While the implementation's error paths are unlikely to be triggered in practice (e.g., clearCartCookies() throwing a non-Error), consider adding at least one error test for associateCartWithUser to verify the internal catch block that clears cookies on carts.associate failure.

💡 Suggested test for associateCartWithUser error handling
it("clears cart cookies when associate fails", async () => {
  const { getAccessToken, clearCartCookies } = await import("@spree/next");
  (getAccessToken as ReturnType<typeof vi.fn>).mockResolvedValue("jwt-token");
  mockClient.carts.associate.mockRejectedValue(new Error("Already associated"));

  const result = await associateCartWithUser();

  expect(clearCartCookies).toHaveBeenCalled();
  expect(result).toEqual({ success: true });
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/data/__tests__/cart.test.ts` around lines 180 - 199, Add a test for
associateCartWithUser that simulates carts.associate throwing to exercise the
catch block: import getAccessToken and clearCartCookies from "@spree/next", mock
getAccessToken to resolve "jwt-token" and mock mockClient.carts.associate to
reject (e.g., new Error("Already associated")), call associateCartWithUser(),
then assert clearCartCookies was called and the function returns { success: true
}; also consider a second test variant where clearCartCookies itself throws a
non-Error value to ensure the fallback path is handled.
src/lib/data/checkout.ts (1)

34-35: Unused cartId parameter in multiple functions.

The cartId parameter is declared but never used in updateOrderAddresses, updateOrderMarket, selectDeliveryRate, applyCode, removeDiscountCode, and removeGiftCard. Instead, the cart ID is obtained via requireCartId(). Consider removing the unused parameter or using it directly to avoid the extra async call.

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

In `@src/lib/data/checkout.ts` around lines 34 - 35, The listed functions
(updateOrderAddresses, updateOrderMarket, selectDeliveryRate, applyCode,
removeDiscountCode, removeGiftCard) declare a cartId parameter but never use
it—currently they always call requireCartId(); fix by either removing the unused
cartId parameter from each signature and callers, or (preferred) use the
provided cartId when present and fall back to await requireCartId() when not;
update the bodies of updateOrderAddresses, updateOrderMarket,
selectDeliveryRate, applyCode, removeDiscountCode, and removeGiftCard to use a
local const id = cartId ?? await requireCartId() (or equivalent), and adjust all
call sites to pass a cartId if they have one or omit it if not.
src/lib/data/customer.ts (1)

158-165: Add explicit return types to the remaining exported actions.

requestPasswordReset() and updateCustomer() still rely on inference, while the rest of this module declares its server-action contract explicitly. In a PR that bumps the SDK surface, leaving these exports inferred makes accidental contract drift easier.

As per coding guidelines, "Use strict TypeScript type checking. Always define explicit return types for functions, use 'satisfies' for type checking object literals, and avoid 'any' (use 'unknown' instead)."

Also applies to: 191-203

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

In `@src/lib/data/customer.ts` around lines 158 - 165, Add explicit return types
for the exported async actions requestPasswordReset and updateCustomer instead
of relying on inference: determine the SDK response types returned by
getClient().passwordResets.create and getClient().customers.update (or import
the corresponding types from your SDK/type declarations) and annotate the
functions with those Promise<...> return types (e.g.,
Promise<PasswordResetResponse> and Promise<CustomerUpdateResponse>) so the
exported server-action contract is explicit; update the function signatures for
requestPasswordReset and updateCustomer accordingly and import any required
types.
🤖 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/components/account/GiftCardList.tsx`:
- Around line 142-148: The additional-info block in GiftCardList.tsx was made
conditional on card.redeemed_at, removing the "Added on" metadata for active
cards; update the JSX that renders the extra info so the container (the div with
classes "pt-4 border-t border-gray-100") is always rendered and always shows the
"Added on {formatDate(card.created_at)}" line, while keeping the "Fully redeemed
on {formatDate(card.redeemed_at)}" paragraph conditional on card.redeemed_at;
locate the block around the card rendering in GiftCardList and split the content
so formatDate(card.created_at) is always displayed and
formatDate(card.redeemed_at) is rendered only when card.redeemed_at is truthy.

In `@src/lib/data/customer.ts`:
- Around line 77-82: The catch blocks currently return arbitrary error.message
to the UI; replace that with a normalized message by creating a helper like
normalizeErrorMessage(error) that maps known auth/validation errors (e.g.,
"Invalid email or password", "User not found", token/validation messages or SDK
AuthError types) to user-friendly strings and returns a stable fallback such as
"Something went wrong. Please try again." for everything else; use this helper
in the return object (the { success: false, error: ... } returns shown) in all
three catch sites, and still log the original error (console/processLogger) for
debugging rather than exposing it to the UI.
- Around line 57-76: The post-authentication steps (merging guest cart and
tagging) are duplicated in login/register but missing from resetPassword;
extract the shared logic into a helper (e.g., finalizeAuthenticatedSession or
postAuthBootstrap) that performs setAccessToken/setRefreshToken handling,
associates guest cart via getCartToken/getCartId and getClient().carts.associate
(catching errors), calls updateTag("customer") and updateTag("cart"), and
returns the same { success: true, user: result.user } shape; then call that
helper from login, register, and resetPassword (and the other indicated blocks
at 108-127 and 178-182) so all sign-in flows run the same post-auth bootstrap.
- Around line 27-34: The catch block in getCustomer is currently clearing tokens
on any error, which logs out users for transient failures; update the error
handling in the getCustomer call that wraps withAuthRefresh so you only
clearAccessToken() and clearRefreshToken() when the error is an authentication
failure (e.g., inspect the thrown error for an auth-specific code/status like
401/403 or an AuthError type after calling
withAuthRefresh/getClient().customer.get) and rethrow or return null for other
errors so transient transport/backend failures are not treated as logout
triggers.

In `@src/lib/data/payment.ts`:
- Around line 39-44: The call to getClient().carts.paymentSessions.complete is
passing undefined as the third argument but the SDK requires a params object
with at least session_result; replace the undefined with a proper params object
(e.g., build a params variable containing session_result and optional
external_data) and pass that instead of undefined; locate the call to
paymentSessions.complete (using id, sessionId, options) and construct params
from available data (or default session_result to "success"/"failure" as
appropriate) before calling complete.

---

Outside diff comments:
In `@src/lib/data/checkout.ts`:
- Around line 90-134: The function applyCode currently calls requireCartId()
outside any try/catch so a thrown redirect/custom error escapes; wrap the call
(and the rest of the function body) in a top-level try/catch or move
requireCartId() inside the existing try so any exception from requireCartId is
caught and returned as { success: false, error: errorMessage(err) }; ensure you
still call getCartOptions(), getClient().carts.* and
updateTag("checkout"/"cart") as before and reference applyCode, requireCartId,
and errorMessage when locating and updating the code.

---

Nitpick comments:
In `@src/lib/data/__tests__/cart.test.ts`:
- Around line 180-199: Add a test for associateCartWithUser that simulates
carts.associate throwing to exercise the catch block: import getAccessToken and
clearCartCookies from "@spree/next", mock getAccessToken to resolve "jwt-token"
and mock mockClient.carts.associate to reject (e.g., new Error("Already
associated")), call associateCartWithUser(), then assert clearCartCookies was
called and the function returns { success: true }; also consider a second test
variant where clearCartCookies itself throws a non-Error value to ensure the
fallback path is handled.

In `@src/lib/data/cached.ts`:
- Around line 2-3: The imports in cached.ts use relative paths; update them to
the project's preferred absolute alias by replacing "./categories" and
"./products" with the corresponding "@/..." module paths (e.g., reference the
modules where getCategory and getProduct are exported using the '@/...' alias)
so getCategory and getProduct are imported via absolute imports for consistency
with project guidelines.

In `@src/lib/data/cart.ts`:
- Around line 83-98: The addToCart function currently calls getCartToken() and
getAccessToken() directly and builds { spreeToken, token } for
getClient().carts.items.create; replace those separate calls with a single call
to getCartOptions() and pass its return value as the options argument to
getClient().carts.items.create (remove the explicit getCartToken/getAccessToken
calls), keeping the rest of addToCart (including updateTag("cart") and returning
{ cart: updatedCart }) intact so it matches updateCartItem/removeCartItem usage.

In `@src/lib/data/checkout.ts`:
- Around line 34-35: The listed functions (updateOrderAddresses,
updateOrderMarket, selectDeliveryRate, applyCode, removeDiscountCode,
removeGiftCard) declare a cartId parameter but never use it—currently they
always call requireCartId(); fix by either removing the unused cartId parameter
from each signature and callers, or (preferred) use the provided cartId when
present and fall back to await requireCartId() when not; update the bodies of
updateOrderAddresses, updateOrderMarket, selectDeliveryRate, applyCode,
removeDiscountCode, and removeGiftCard to use a local const id = cartId ?? await
requireCartId() (or equivalent), and adjust all call sites to pass a cartId if
they have one or omit it if not.

In `@src/lib/data/customer.ts`:
- Around line 158-165: Add explicit return types for the exported async actions
requestPasswordReset and updateCustomer instead of relying on inference:
determine the SDK response types returned by getClient().passwordResets.create
and getClient().customers.update (or import the corresponding types from your
SDK/type declarations) and annotate the functions with those Promise<...> return
types (e.g., Promise<PasswordResetResponse> and Promise<CustomerUpdateResponse>)
so the exported server-action contract is explicit; update the function
signatures for requestPasswordReset and updateCustomer accordingly and import
any required types.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8ee0cddb-803c-462c-885c-78f2095059a0

📥 Commits

Reviewing files that changed from the base of the PR and between c7db4b0 and 4fc9d6a.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (26)
  • README.md
  • package.json
  • src/app/[country]/[locale]/(checkout)/checkout/[id]/page.tsx
  • src/app/[country]/[locale]/(storefront)/products/[slug]/ProductDetails.tsx
  • src/components/account/GiftCardList.tsx
  • src/components/checkout/PaymentSection.tsx
  • src/components/products/ProductCustomFields.tsx
  • src/lib/data/__tests__/cart.test.ts
  • src/lib/data/__tests__/checkout.test.ts
  • src/lib/data/__tests__/customer.test.ts
  • src/lib/data/__tests__/payment.test.ts
  • src/lib/data/addresses.ts
  • src/lib/data/cached.ts
  • src/lib/data/cart.ts
  • src/lib/data/categories.ts
  • src/lib/data/checkout.ts
  • src/lib/data/cookies.ts
  • src/lib/data/countries.ts
  • src/lib/data/credit-cards.ts
  • src/lib/data/customer.ts
  • src/lib/data/gift-cards.ts
  • src/lib/data/markets.ts
  • src/lib/data/orders.ts
  • src/lib/data/payment.ts
  • src/lib/data/products.ts
  • src/lib/seo.ts
💤 Files with no reviewable changes (1)
  • src/components/checkout/PaymentSection.tsx

Comment on lines +142 to +148
{card.redeemed_at && (
<div className="pt-4 border-t border-gray-100">
<p className="text-sm text-gray-500">
Fully redeemed on {formatDate(card.redeemed_at)}
</p>
</div>
</div>
)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Restore “Added on” metadata for non-redeemed cards.

Lines 142-148 now hide the entire additional-info section unless redeemed_at exists, so non-redeemed cards lose useful timeline context.

💡 Suggested patch
-      {card.redeemed_at && (
-        <div className="pt-4 border-t border-gray-100">
-          <p className="text-sm text-gray-500">
-            Fully redeemed on {formatDate(card.redeemed_at)}
-          </p>
-        </div>
-      )}
+      <div className="pt-4 border-t border-gray-100 space-y-1">
+        <p className="text-sm text-gray-500">
+          Added on {formatDate(card.created_at)}
+        </p>
+        {card.redeemed_at && (
+          <p className="text-sm text-gray-500">
+            Fully redeemed on {formatDate(card.redeemed_at)}
+          </p>
+        )}
+      </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/account/GiftCardList.tsx` around lines 142 - 148, The
additional-info block in GiftCardList.tsx was made conditional on
card.redeemed_at, removing the "Added on" metadata for active cards; update the
JSX that renders the extra info so the container (the div with classes "pt-4
border-t border-gray-100") is always rendered and always shows the "Added on
{formatDate(card.created_at)}" line, while keeping the "Fully redeemed on
{formatDate(card.redeemed_at)}" paragraph conditional on card.redeemed_at;
locate the block around the card rendering in GiftCardList and split the content
so formatDate(card.created_at) is always displayed and
formatDate(card.redeemed_at) is rendered only when card.redeemed_at is truthy.

Comment thread src/lib/data/customer.ts
Comment thread src/lib/data/customer.ts Outdated
Comment thread src/lib/data/customer.ts
Comment thread src/lib/data/payment.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/lib/data/customer.ts (1)

92-97: ⚠️ Potential issue | 🟡 Minor

Normalize the auth errors before returning them to the UI.

These catch blocks still forward arbitrary error.message values. Network/SDK/backend failures will show up verbatim on account screens; map known auth/validation cases and fall back to a stable generic message for everything else.

Also applies to: 125-129, 181-185

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

In `@src/lib/data/customer.ts` around lines 92 - 97, The catch blocks in
src/lib/data/customer.ts that currently return error instanceof Error ?
error.message : "Invalid email or password" should be replaced with a normalized
error flow: add a helper function normalizeAuthError(error) that maps known
error messages/codes (e.g., invalid_credentials, user_not_found, weak_password,
network/error codes from your auth SDK) to user-safe strings and returns a
stable generic fallback like "Unable to authenticate. Please try again." Then
update the three catch sites (the catch in the sign-in, sign-up and
password-reset flows at the shown locations) to call normalizeAuthError(error)
and return { success: false, error: normalizeAuthError(error) } instead of
exposing raw error.message.
🧹 Nitpick comments (1)
src/lib/data/customer.ts (1)

25-45: Add explicit return types to these server helpers/actions.

finalizeAuth, requestPasswordReset, and updateCustomer all rely on inference right now. This file is the SDK boundary, so explicit return contracts make upgrade drift visible at compile time instead of leaking through callers. As per coding guidelines, "Use strict TypeScript type checking. Always define explicit return types for functions, use 'satisfies' for type checking object literals, and avoid 'any' (use 'unknown' instead)."

Also applies to: 155-163, 189-200

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

In `@src/lib/data/customer.ts` around lines 25 - 45, Annotate the async helper
functions with explicit return types instead of relying on inference: declare
finalizeAuth(): Promise<void>, and determine and add precise Promise<...> return
types for requestPasswordReset and updateCustomer that match their actual
returned payloads; update any other server helper/action signatures in this file
to have explicit return types as well, and replace any use of implicit any in
catches/returns with unknown (and narrow/throw as needed) so the SDK boundary
has strict, visible contracts.
🤖 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/lib/data/customer.ts`:
- Around line 32-40: The catch block for getClient().carts.associate(cartId, {
token, spreeToken: cartToken }) currently swallows errors and leaves the stale
guest cart cookie; update the catch to clear the guest cart cookies the same way
as src/lib/data/cart.ts (lines 93-112) does so the old cartToken is removed on
association failure. Specifically, in the catch for carts.associate reference
and call the cookie-clearing routine used in cart.ts (or import/implement a
clearGuestCartCookies/clearCartCookies helper) to remove cartToken/cartId
cookies instead of silently ignoring the error.
- Around line 194-198: The updateCustomer() flow calls withAuthRefresh() inside
actionResult() but doesn't clear stale auth cookies on authorization failures;
modify the actionResult callback around getClient().customer.update(...) so it
catches rejections from withAuthRefresh(), detects auth failures (HTTP 401/403
or the specific auth error type returned by withAuthRefresh()), clears the
terminal auth cookies/tokens (e.g., invoke the existing
clearAuthTokens/clearAuthCookies helper or delete the same cookies set during
sign-in), then rethrow or return the error so actionResult can surface the
failure; keep the updateTag("customer") behavior after successful update.

---

Duplicate comments:
In `@src/lib/data/customer.ts`:
- Around line 92-97: The catch blocks in src/lib/data/customer.ts that currently
return error instanceof Error ? error.message : "Invalid email or password"
should be replaced with a normalized error flow: add a helper function
normalizeAuthError(error) that maps known error messages/codes (e.g.,
invalid_credentials, user_not_found, weak_password, network/error codes from
your auth SDK) to user-safe strings and returns a stable generic fallback like
"Unable to authenticate. Please try again." Then update the three catch sites
(the catch in the sign-in, sign-up and password-reset flows at the shown
locations) to call normalizeAuthError(error) and return { success: false, error:
normalizeAuthError(error) } instead of exposing raw error.message.

---

Nitpick comments:
In `@src/lib/data/customer.ts`:
- Around line 25-45: Annotate the async helper functions with explicit return
types instead of relying on inference: declare finalizeAuth(): Promise<void>,
and determine and add precise Promise<...> return types for requestPasswordReset
and updateCustomer that match their actual returned payloads; update any other
server helper/action signatures in this file to have explicit return types as
well, and replace any use of implicit any in catches/returns with unknown (and
narrow/throw as needed) so the SDK boundary has strict, visible contracts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6c9003bc-11ba-4082-864d-31c4014bd090

📥 Commits

Reviewing files that changed from the base of the PR and between 4fc9d6a and e2fb54b.

📒 Files selected for processing (2)
  • src/lib/data/__tests__/customer.test.ts
  • src/lib/data/customer.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/data/tests/customer.test.ts

Comment thread src/lib/data/customer.ts
Comment thread src/lib/data/customer.ts
@damianlegawiec
damianlegawiec merged commit b07d581 into main Mar 30, 2026
4 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Apr 9, 2026
12 tasks
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.

1 participant