Skip to content

Bump SDK to 0.16#83

Merged
damianlegawiec merged 6 commits into
mainfrom
chore/sdk-0-16
Mar 25, 2026
Merged

Bump SDK to 0.16#83
damianlegawiec merged 6 commits into
mainfrom
chore/sdk-0-16

Conversation

@damianlegawiec
Copy link
Copy Markdown
Member

@damianlegawiec damianlegawiec commented Mar 25, 2026

  • Support Gift Cards on checkout
  • Improve Order Placed page

Summary by CodeRabbit

  • New Features

    • New totals, address and payment display components; improved order confirmation layout
    • Gift card support added across checkout, cart and order totals
  • Improvements

    • Coupon/code entry now handles discount codes and gift cards with clearer apply/remove controls and messaging (“Invalid code”)
    • Order summary shows gift card or store credit lines and an "Amount due" section when applicable
  • Tests

    • Expanded checkout tests covering discount/gift-card apply and removal flows
  • Chores

    • Updated Spree package versions

* Support Gift Cards on checkout
* Improve Order Placed page
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 25, 2026

Warning

Rate limit exceeded

@damianlegawiec has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 23 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9cc3833f-895b-478a-a6a3-b8c9730fe849

📥 Commits

Reviewing files that changed from the base of the PR and between 436044f and f3aa1bd.

📒 Files selected for processing (3)
  • src/components/order/AddressBlock.tsx
  • src/lib/data/__tests__/checkout.test.ts
  • src/lib/data/checkout.ts

Walkthrough

Unified coupon handling: SDK deps bumped; checkout data-layer now exposes applyCode (tries discount then gift card) plus removeDiscountCode/removeGiftCard; UI and tests updated to surface gift-card/store-credit lines and amount-due rows and to call the new APIs.

Changes

Cohort / File(s) Summary
Dependency Updates
package.json
Bumped @spree/next and @spree/sdk from ^0.15.0 to ^0.16.0.
Data layer & tests
src/lib/data/checkout.ts, src/lib/data/__tests__/checkout.test.ts
Replaced coupon-specific APIs with unified code handling: added applyCode (attempts discount, falls back to gift card), added removeDiscountCode and removeGiftCard, removed coupon-only functions; updated tests and mocks to cover fallback/error cases and SpreeError status handling.
Checkout page wiring
src/app/[country]/[locale]/(checkout)/checkout/[id]/page.tsx
Replaced coupon callbacks with onApplyCode, onRemoveDiscount, onRemoveGiftCard; handlers call new data-layer functions and refresh cart from returned payload.
Checkout components
src/components/checkout/CouponCode.tsx, src/components/checkout/Summary.tsx
Split removal handlers into onRemoveDiscount / onRemoveGiftCard; added applied gift card row; conditional rendering for gift-card vs store-credit deductions and an Amount due row.
Cart & Order displays
src/app/[country]/[locale]/(storefront)/cart/page.tsx, src/components/account/OrderDetail.tsx, src/app/[country]/[locale]/(checkout)/order-placed/[id]/page.tsx
Added gift card / store credit negative line items and Amount due when applicable; refactored order-placed layout to use OrderTotals, PaymentInfo, and AddressBlock; updated payment and shipping rendering.
New UI components
src/components/order/OrderTotals.tsx, src/components/order/PaymentInfo.tsx, src/components/order/AddressBlock.tsx
Added OrderTotals, PaymentInfo, and AddressBlock components to centralize totals, payment, and address rendering used by order and checkout pages.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client (browser)
  participant CheckoutPage as Checkout Page UI
  participant DataLayer as Checkout Data Layer
  participant SpreeAPI as Spree SDK / API

  Client->>CheckoutPage: submit code
  CheckoutPage->>DataLayer: applyCode(cartId, code)
  DataLayer->>SpreeAPI: applyDiscountCode(code)
  alt discount success
    SpreeAPI-->>DataLayer: { success, cart }
    DataLayer-->>CheckoutPage: { success, type: "discount", cart }
  else discount not found (422/404)
    SpreeAPI-->>DataLayer: error(422)
    DataLayer->>SpreeAPI: applyGiftCard(code)
    alt gift card success
      SpreeAPI-->>DataLayer: { success, cart }
      DataLayer-->>CheckoutPage: { success, type: "gift_card", cart }
    else both fail
      SpreeAPI-->>DataLayer: error
      DataLayer-->>CheckoutPage: { success: false, error }
    end
  else discount error (non-422)
    SpreeAPI-->>DataLayer: error
    DataLayer-->>CheckoutPage: { success: false, error }
  end
  CheckoutPage-->>Client: update UI with new cart state / error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I nibbled at code, one hop then two,

Discounts first, then gift cards too,
Totals tidy, amounts on view,
Cart refreshed — a hoppity woohoo!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Bump SDK to 0.16' accurately reflects the primary dependency update in package.json, but significantly understates the scope of the changeset, which includes substantial feature additions (Gift Card support), UI refactoring (componentization), and order page improvements.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/sdk-0-16

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

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

Caution

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

⚠️ Outside diff range comments (1)
src/components/checkout/CouponCode.tsx (1)

37-52: ⚠️ Potential issue | 🟠 Major

Async handlers can leave UI stuck on failures.

If onApply / onRemoveDiscount / onRemoveGiftCard throws, loading state is never reset.

🛠️ Proposed hardening with try/catch/finally
-  const handleApply = async (e: React.FormEvent) => {
+  const handleApply = async (e: React.FormEvent): Promise<void> => {
     e.preventDefault();
-    if (!code.trim()) return;
+    const trimmedCode = code.trim();
+    if (!trimmedCode) return;

     setApplying(true);
     setError(null);

-    const result = await onApply(code.trim());
-    if (result.success) {
-      setCode("");
-    } else {
-      setError(result.error || "Invalid code");
+    try {
+      const result = await onApply(trimmedCode);
+      if (result.success) {
+        setCode("");
+      } else {
+        setError(result.error || "Invalid code");
+      }
+    } catch {
+      setError("Failed to apply code");
+    } finally {
+      setApplying(false);
     }
-
-    setApplying(false);
   };

-  const handleRemoveDiscount = async (discountCode: string) => {
+  const handleRemoveDiscount = async (discountCode: string): Promise<void> => {
     setRemoving(discountCode);
     setError(null);

-    const result = await onRemoveDiscount(discountCode);
-    if (!result.success) {
-      setError(result.error || "Failed to remove discount code");
+    try {
+      const result = await onRemoveDiscount(discountCode);
+      if (!result.success) {
+        setError(result.error || "Failed to remove discount code");
+      }
+    } catch {
+      setError("Failed to remove discount code");
+    } finally {
+      setRemoving(null);
     }
-
-    setRemoving(null);
   };

-  const handleRemoveGiftCard = async () => {
+  const handleRemoveGiftCard = async (): Promise<void> => {
     if (!appliedGiftCard) return;

     setRemoving(appliedGiftCard.id);
     setError(null);

-    const result = await onRemoveGiftCard(appliedGiftCard.id);
-    if (!result.success) {
-      setError(result.error || "Failed to remove gift card");
+    try {
+      const result = await onRemoveGiftCard(appliedGiftCard.id);
+      if (!result.success) {
+        setError(result.error || "Failed to remove gift card");
+      }
+    } catch {
+      setError("Failed to remove gift card");
+    } finally {
+      setRemoving(null);
     }
-
-    setRemoving(null);
   };

Also applies to: 54-64, 66-78

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

In `@src/components/checkout/CouponCode.tsx` around lines 37 - 52, The async
handlers (handleApply and the similar handlers for removal: handleRemoveDiscount
and handleRemoveGiftCard) can throw and leave the UI stuck; wrap the await
onApply/onRemoveDiscount/onRemoveGiftCard calls in try/catch/finally blocks so
any thrown exception is caught, setError is updated on failure (using the caught
error message or a default), and the loading state (setApplying / setLoading /
setRemoving as used in each handler) is always reset in the finally block;
update each handler to use try { const result = await ...; handle success path
(clear code / update state) } catch (err) { setError(err?.message || "Operation
failed") } finally { reset the corresponding loading flag }.
🧹 Nitpick comments (2)
src/lib/data/checkout.ts (1)

82-120: Add explicit return types to the new checkout actions.

applyCode now returns a non-trivial discriminated union, and the two remove actions are part of the same public server-action surface. Please pin these return types explicitly so downstream handlers do not widen silently as this flow evolves.

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)."

🤖 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 82 - 120, Add explicit return type
annotations: declare a discriminated union type (e.g. CheckoutApplyResult = {
success: true; cart: Cart; type: "discount" | "gift_card" } | { success: false;
error: string }) and annotate applyCode as async function applyCode(cartId:
string, code: string): Promise<CheckoutApplyResult>. Also add explicit return
types for removeDiscountCode and removeGiftCard (e.g. Promise<ActionResult<{
cart: Cart }>> or the project’s existing action result type) and ensure returned
object literals use satisfies for shape checking and avoid any by using unknown
when needed; update the signatures for removeDiscountCode and removeGiftCard to
include those explicit Promise return types and keep references to
_applyDiscountCode, _applyGiftCard, _removeDiscountCode, and _removeGiftCard
unchanged.
src/components/checkout/CouponCode.tsx (1)

20-25: Add explicit return type to the component export.

For strict TS consistency, type the component return explicitly.

✍️ Suggested typing
 export function CouponCode({
   cart,
   onApply,
   onRemoveDiscount,
   onRemoveGiftCard,
-}: CouponCodeProps) {
+}: CouponCodeProps): JSX.Element {

As per coding guidelines, "Use strict TypeScript type checking. Always define explicit return types for functions."

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

In `@src/components/checkout/CouponCode.tsx` around lines 20 - 25, The component
export CouponCode lacks an explicit return type; update its signature to include
a strict React return type (e.g., JSX.Element or React.ReactElement) so it reads
like CouponCode(...): JSX.Element, and add an import for the React types if not
already present; keep CouponCodeProps as-is and ensure the implementation still
returns a valid JSX element.
🤖 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]/(checkout)/order-placed/[id]/page.tsx:
- Around line 278-287: Replace the use of the order-level flag when rendering
each payment row and instead derive the label from the payment object: check
payment.source_type on the payment (e.g., payment.source_type ===
"Spree::GiftCard" ? "Gift Card" : "Store Credit") where the current code uses
order.gift_card, leaving payment.display_amount and other fields as-is; ensure
the conditional that currently branches on payment.source_type ===
"store_credit" && payment.source still renders but the displayed label comes
from payment.source_type to correctly distinguish "Spree::GiftCard" vs
"Spree::StoreCredit".

In `@src/components/checkout/CouponCode.tsx`:
- Line 80: The visibility check for the discount input uses OR and hides the
field when either coupons or a gift card exist; change the boolean logic in the
hasAppliedCode declaration to use AND so the input only hides when both
couponPromotions and appliedGiftCard are present. Locate the hasAppliedCode
constant in CouponCode.tsx (references: couponPromotions, appliedGiftCard,
hasAppliedCode) and update the expression accordingly, then run UI to verify
both types can be applied simultaneously.

In `@src/lib/data/checkout.ts`:
- Around line 87-103: The applyCode function needs an explicit return type and
must only fall back to _applyGiftCard when the discount failure is a SpreeError
indicating an invalid/not-found discount; change the signature of applyCode to
include the proper return type (the union shape currently returned: success true
with cart and type, or success false with error), and in the catch of
_applyDiscountCode only call _applyGiftCard when discountError instanceof
SpreeError and discountError.code matches the invalid/not-found codes you use
(e.g., "discount_not_found"/"invalid_discount"); otherwise immediately return
the original discountError message; update or add a unit test to cover the
SpreeError code path so the gift-card retry happens only for that specific error
code.

---

Outside diff comments:
In `@src/components/checkout/CouponCode.tsx`:
- Around line 37-52: The async handlers (handleApply and the similar handlers
for removal: handleRemoveDiscount and handleRemoveGiftCard) can throw and leave
the UI stuck; wrap the await onApply/onRemoveDiscount/onRemoveGiftCard calls in
try/catch/finally blocks so any thrown exception is caught, setError is updated
on failure (using the caught error message or a default), and the loading state
(setApplying / setLoading / setRemoving as used in each handler) is always reset
in the finally block; update each handler to use try { const result = await ...;
handle success path (clear code / update state) } catch (err) {
setError(err?.message || "Operation failed") } finally { reset the corresponding
loading flag }.

---

Nitpick comments:
In `@src/components/checkout/CouponCode.tsx`:
- Around line 20-25: The component export CouponCode lacks an explicit return
type; update its signature to include a strict React return type (e.g.,
JSX.Element or React.ReactElement) so it reads like CouponCode(...):
JSX.Element, and add an import for the React types if not already present; keep
CouponCodeProps as-is and ensure the implementation still returns a valid JSX
element.

In `@src/lib/data/checkout.ts`:
- Around line 82-120: Add explicit return type annotations: declare a
discriminated union type (e.g. CheckoutApplyResult = { success: true; cart:
Cart; type: "discount" | "gift_card" } | { success: false; error: string }) and
annotate applyCode as async function applyCode(cartId: string, code: string):
Promise<CheckoutApplyResult>. Also add explicit return types for
removeDiscountCode and removeGiftCard (e.g. Promise<ActionResult<{ cart: Cart
}>> or the project’s existing action result type) and ensure returned object
literals use satisfies for shape checking and avoid any by using unknown when
needed; update the signatures for removeDiscountCode and removeGiftCard to
include those explicit Promise return types and keep references to
_applyDiscountCode, _applyGiftCard, _removeDiscountCode, and _removeGiftCard
unchanged.
🪄 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: 5dae26e7-d530-4019-ad3f-aae16f4944c5

📥 Commits

Reviewing files that changed from the base of the PR and between 15b8d5a and 6c6fc55.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • package.json
  • src/app/[country]/[locale]/(checkout)/checkout/[id]/page.tsx
  • src/app/[country]/[locale]/(checkout)/order-placed/[id]/page.tsx
  • src/app/[country]/[locale]/(storefront)/cart/page.tsx
  • src/components/account/OrderDetail.tsx
  • src/components/checkout/CouponCode.tsx
  • src/components/checkout/Summary.tsx
  • src/lib/data/__tests__/checkout.test.ts
  • src/lib/data/checkout.ts

Comment thread src/app/[country]/[locale]/(checkout)/order-placed/[id]/page.tsx Outdated
Comment thread src/components/checkout/CouponCode.tsx Outdated
Comment thread src/lib/data/checkout.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/checkout.ts`:
- Around line 101-108: The fallback catch block currently returns the stale
discountError message; change it to prefer the gift-card failure details
instead: when catching giftCardError in the block that calls _applyGiftCard, set
error to giftCardError.message if giftCardError is an Error, otherwise fall back
to "Invalid code" (or normalize both to "Invalid code" if you want uniform UX).
Update the return to use that computed error (referencing _applyGiftCard,
giftCardError, and discountError so you only replace the discountError usage in
this catch).
🪄 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: 13ac3f4a-f847-4f41-bd7b-09062bff3a62

📥 Commits

Reviewing files that changed from the base of the PR and between 6c6fc55 and e66930f.

📒 Files selected for processing (3)
  • src/components/checkout/CouponCode.tsx
  • src/lib/data/__tests__/checkout.test.ts
  • src/lib/data/checkout.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/checkout/CouponCode.tsx

Comment thread src/lib/data/checkout.ts Outdated
Copy link
Copy Markdown

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

🤖 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/order/AddressBlock.tsx`:
- Around line 14-16: The city/state/postal line currently interpolates
address.city, address.state_text and address.postal_code with fixed separators
which produces awkward output when fields are empty; update the rendering in
AddressBlock (the component using address.city, address.state_text,
address.postal_code) to build the line by collecting those three pieces into an
array, filtering out empty/undefined values (e.g., filter(Boolean)), and joining
with the proper separators (e.g., ", " between city and the rest or " " between
state and postal) so only existing parts are shown and no stray commas/undefined
appear.

In `@src/components/order/OrderTotals.tsx`:
- Around line 46-61: The current JSX uses a chained ternary so the store credit
branch is skipped whenever gift card is truthy; update the OrderTotals component
to render the gift-card and store-credit deduction blocks independently by
replacing the ternary with two separate conditional renders (e.g., render the
gift card block when order.gift_card && Number.parseFloat(order.gift_card_total)
> 0, and separately render the store credit block when order.store_credit_total
&& Number.parseFloat(order.store_credit_total) > 0), keeping the existing markup
and display fields (order.display_gift_card_total,
order.display_store_credit_total) so both deductions can appear together in the
breakdown.
🪄 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: dc1ccd64-6165-43a9-8106-f8d72bc3a1ee

📥 Commits

Reviewing files that changed from the base of the PR and between e66930f and 436044f.

📒 Files selected for processing (6)
  • src/app/[country]/[locale]/(checkout)/order-placed/[id]/page.tsx
  • src/components/account/OrderDetail.tsx
  • src/components/order/AddressBlock.tsx
  • src/components/order/OrderTotals.tsx
  • src/components/order/PaymentInfo.tsx
  • src/lib/data/__tests__/checkout.test.ts

Comment thread src/components/order/AddressBlock.tsx
Comment thread src/components/order/OrderTotals.tsx
@damianlegawiec damianlegawiec merged commit 75fdc64 into main Mar 25, 2026
4 checks passed
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