Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
24a24da
Update package-lock.json for @shopify/cli dependency update
juanpprieto Aug 15, 2025
4dcd80c
[2025-07] Fix Monorepo RR7 CI build failures and improve reliability …
juanpprieto Aug 20, 2025
be2a6cb
chore: regenerate cookbook recipes with updated patch hashes
juanpprieto Aug 20, 2025
b819b56
[2025-07] Fix React Context error during client-side hydration (#3101)
juanpprieto Aug 21, 2025
862e2bb
Update Vitest from v1.0.4 to v3.2.4 across all packages (#3126)
kdaviduik Aug 27, 2025
7020e41
[2025-06] Fix MiniOxygen/Miniflare v4 redirects handling + Stable --c…
juanpprieto Aug 28, 2025
a21ec94
Update subscriptions recipe with account/subscriptions (#3124)
juanpprieto Aug 29, 2025
8e0f515
Delete unused variable
kdaviduik Sep 11, 2025
b273fa9
2025-07 generated types
juanpprieto Aug 27, 2025
d27859d
Add cartGiftCardCodesRemove mutation support
juanpprieto Aug 27, 2025
e3af043
Fix linting issues: sort exports and remove duplicate imports
juanpprieto Aug 28, 2025
cc12528
Fix tests for cartGiftCardCodesRemove mutation support
juanpprieto Aug 28, 2025
1a5304f
Add missing appliedGiftCards id field
kdaviduik Sep 11, 2025
6384e78
fixing up after rebase
rbshop Sep 16, 2025
165e84d
Undoing an accidental non-change from the rebase
rbshop Sep 16, 2025
1baad5f
Merge branch 'main' into feat-cartGiftCardCodesRemove
juanpprieto Sep 16, 2025
8a89142
Merge branch 'main' into feat-cartGiftCardCodesRemove
juanpprieto Sep 16, 2025
961793f
Update lock
juanpprieto Sep 16, 2025
d0f548a
Update multipass recipe
juanpprieto Sep 16, 2025
779a606
fix multipass
juanpprieto Sep 16, 2025
0bc7ca0
Fix multipass recipe for gift card removal feature and enhance patch …
juanpprieto Sep 16, 2025
6a64bf6
fix: resolve cookbook recipe validation failures for custom-cart-meth…
juanpprieto Sep 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .changeset/gift-card-removal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
'@shopify/hydrogen': patch
'@shopify/cli-hydrogen': patch
---

Add support for removing individual gift cards from cart

The `cartGiftCardCodesUpdate` mutation requires all gift card codes to be provided, but the API only returns the last 4 digits for security. This made it impossible to remove specific gift cards when multiple were applied.

This fix introduces the missing `cartGiftCardCodesRemove` mutation to remove gift cards by their IDs.

**What changed:**

**In `@shopify/hydrogen`:**

- Added `cartGiftCardCodesRemoveDefault` mutation handler
- Added `removeGiftCardCodes` method to `HydrogenCart`
- Added `GiftCardCodesRemove` action to `CartForm.ACTIONS`
- Updated default cart fragment to include `appliedGiftCards` with `id` field

**In the skeleton template:**

- Added `id` field to `appliedGiftCards` fragment
- Updated `CartSummary` to show individual remove buttons per gift card
- Added handler for `GiftCardCodesRemove` action in cart route
- Maintained client-side tracking for additive gift card updates

**Usage example:**

```tsx
// In your cart component
function RemoveGiftCardButton({giftCardId}: {giftCardId: string}) {
return (
<CartForm
route="/cart"
action={CartForm.ACTIONS.GiftCardCodesRemove}
inputs={{
giftCardCodes: [giftCardId], // Pass the gift card ID
}}
>
<button type="submit">Remove</button>
</CartForm>
);
}
```

**Cart handler usage:**

```ts
// The cart handler now includes removeGiftCardCodes method
const cart = createCartHandler({...});

// Remove specific gift cards by their IDs
const result = await cart.removeGiftCardCodes(['giftCardId1', 'giftCardId2']);
```
98 changes: 49 additions & 49 deletions cookbook/llms/metaobjects.prompt.md

Large diffs are not rendered by default.

30 changes: 15 additions & 15 deletions cookbook/llms/multipass.prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ Key features:



#### File: [MultipassCheckoutButton.tsx](https://github.com/Shopify/hydrogen/blob/6681f92e84d42b5a6aca153fb49e31dcd8af84f6/cookbook/recipes/multipass/ingredients/templates/skeleton/app/components/MultipassCheckoutButton.tsx)
#### File: [MultipassCheckoutButton.tsx](https://github.com/Shopify/hydrogen/blob/75623a5bfdd8d6f0eab0d3547860341c20d9076c/cookbook/recipes/multipass/ingredients/templates/skeleton/app/components/MultipassCheckoutButton.tsx)

```tsx
import React, {useCallback} from 'react';
Expand Down Expand Up @@ -294,7 +294,7 @@ export function MultipassCheckoutButton(props: MultipassCheckoutButtonProps) {

type CartSummaryProps = {
cart: OptimisticCart<CartApiQueryFragment | null>;
@@ -58,9 +60,10 @@ function CartCheckoutActions({checkoutUrl}: {checkoutUrl?: string}) {
@@ -59,9 +61,10 @@ function CartCheckoutActions({checkoutUrl}: {checkoutUrl?: string}) {

return (
<div>
Expand All @@ -313,7 +313,7 @@ export function MultipassCheckoutButton(props: MultipassCheckoutButtonProps) {



#### File: [multipass.ts](https://github.com/Shopify/hydrogen/blob/6681f92e84d42b5a6aca153fb49e31dcd8af84f6/cookbook/recipes/multipass/ingredients/templates/skeleton/app/lib/multipass/multipass.ts)
#### File: [multipass.ts](https://github.com/Shopify/hydrogen/blob/75623a5bfdd8d6f0eab0d3547860341c20d9076c/cookbook/recipes/multipass/ingredients/templates/skeleton/app/lib/multipass/multipass.ts)

```ts
import type {
Expand Down Expand Up @@ -463,7 +463,7 @@ export async function multipass(



#### File: [multipassify.server.ts](https://github.com/Shopify/hydrogen/blob/6681f92e84d42b5a6aca153fb49e31dcd8af84f6/cookbook/recipes/multipass/ingredients/templates/skeleton/app/lib/multipass/multipassify.server.ts)
#### File: [multipassify.server.ts](https://github.com/Shopify/hydrogen/blob/75623a5bfdd8d6f0eab0d3547860341c20d9076c/cookbook/recipes/multipass/ingredients/templates/skeleton/app/lib/multipass/multipassify.server.ts)

```ts
import CryptoJS from 'crypto-js';
Expand Down Expand Up @@ -672,7 +672,7 @@ export class Multipassify {



#### File: [types.ts](https://github.com/Shopify/hydrogen/blob/6681f92e84d42b5a6aca153fb49e31dcd8af84f6/cookbook/recipes/multipass/ingredients/templates/skeleton/app/lib/multipass/types.ts)
#### File: [types.ts](https://github.com/Shopify/hydrogen/blob/75623a5bfdd8d6f0eab0d3547860341c20d9076c/cookbook/recipes/multipass/ingredients/templates/skeleton/app/lib/multipass/types.ts)

```ts
/*
Expand Down Expand Up @@ -766,7 +766,7 @@ export interface MultipassTokenResponseType {



#### File: [account_.activate.$id.$activationToken.tsx](https://github.com/Shopify/hydrogen/blob/6681f92e84d42b5a6aca153fb49e31dcd8af84f6/cookbook/recipes/multipass/ingredients/templates/skeleton/app/routes/account_.activate.$id.$activationToken.tsx)
#### File: [account_.activate.$id.$activationToken.tsx](https://github.com/Shopify/hydrogen/blob/75623a5bfdd8d6f0eab0d3547860341c20d9076c/cookbook/recipes/multipass/ingredients/templates/skeleton/app/routes/account_.activate.$id.$activationToken.tsx)

```tsx
import {Form, useActionData, data, redirect} from 'react-router';
Expand Down Expand Up @@ -1464,25 +1464,25 @@ const CUSTOMER_ACTIVATE_MUTATION = `#graphql



#### File: [account_.login.multipass.tsx](https://github.com/Shopify/hydrogen/blob/6681f92e84d42b5a6aca153fb49e31dcd8af84f6/cookbook/recipes/multipass/ingredients/templates/skeleton/app/routes/account_.login.multipass.tsx)
#### File: [account_.login.multipass.tsx](https://github.com/Shopify/hydrogen/blob/75623a5bfdd8d6f0eab0d3547860341c20d9076c/cookbook/recipes/multipass/ingredients/templates/skeleton/app/routes/account_.login.multipass.tsx)

```tsx
import {data as remixData, redirect} from 'react-router';
import type {Route} from './+types/account_.login.multipass';
import type {LoaderFunctionArgs, ActionFunctionArgs, HeadersFunction} from 'react-router';
import {Multipassify} from '~/lib/multipass/multipassify.server';
import type {
CustomerInfoType,
MultipassRequestBody,
NotLoggedInResponseType,
} from '~/lib/multipass/types';

export const headers: Route.HeadersFunction = ({actionHeaders}) =>
export const headers: HeadersFunction = ({actionHeaders}) =>
actionHeaders;

/*
Redirect document GET requests to the login page (housekeeping)
*/
export async function loader({params, context}: Route.LoaderArgs) {
export async function loader({params, context}: LoaderFunctionArgs) {
const customerAccessToken = context.session.get('customerAccessToken');

if (customerAccessToken) {
Expand All @@ -1496,7 +1496,7 @@ export async function loader({params, context}: Route.LoaderArgs) {
Handles POST requests to `/account/login/multipass`
expects body: { return_to?: string, customer }
*/
export async function action({request, context}: Route.ActionArgs) {
export async function action({request, context}: ActionFunctionArgs) {
const {session, storefront, env} = context;
const origin = request.headers.get('Origin') || '';
const isOptionsReq = request.method === 'OPTIONS';
Expand Down Expand Up @@ -2127,7 +2127,7 @@ const CUSTOMER_INFO_QUERY = `#graphql



#### File: [account_.recover.tsx](https://github.com/Shopify/hydrogen/blob/6681f92e84d42b5a6aca153fb49e31dcd8af84f6/cookbook/recipes/multipass/ingredients/templates/skeleton/app/routes/account_.recover.tsx)
#### File: [account_.recover.tsx](https://github.com/Shopify/hydrogen/blob/75623a5bfdd8d6f0eab0d3547860341c20d9076c/cookbook/recipes/multipass/ingredients/templates/skeleton/app/routes/account_.recover.tsx)

```tsx
import {Form, Link, useActionData, data, redirect} from 'react-router';
Expand Down Expand Up @@ -2485,7 +2485,7 @@ const CUSTOMER_RECOVER_MUTATION = `#graphql



#### File: [account_.register.tsx](https://github.com/Shopify/hydrogen/blob/6681f92e84d42b5a6aca153fb49e31dcd8af84f6/cookbook/recipes/multipass/ingredients/templates/skeleton/app/routes/account_.register.tsx)
#### File: [account_.register.tsx](https://github.com/Shopify/hydrogen/blob/75623a5bfdd8d6f0eab0d3547860341c20d9076c/cookbook/recipes/multipass/ingredients/templates/skeleton/app/routes/account_.register.tsx)

```tsx
import {Form, Link, useActionData, data, redirect} from 'react-router';
Expand Down Expand Up @@ -2697,7 +2697,7 @@ const REGISTER_LOGIN_MUTATION = `#graphql



#### File: [account_.reset.$id.$resetToken.tsx](https://github.com/Shopify/hydrogen/blob/6681f92e84d42b5a6aca153fb49e31dcd8af84f6/cookbook/recipes/multipass/ingredients/templates/skeleton/app/routes/account_.reset.$id.$resetToken.tsx)
#### File: [account_.reset.$id.$resetToken.tsx](https://github.com/Shopify/hydrogen/blob/75623a5bfdd8d6f0eab0d3547860341c20d9076c/cookbook/recipes/multipass/ingredients/templates/skeleton/app/routes/account_.reset.$id.$resetToken.tsx)

```tsx
import {data, Form, redirect, useActionData} from 'react-router';
Expand Down Expand Up @@ -3264,7 +3264,7 @@ const CUSTOMER_RESET_MUTATION = `#graphql

const {action, inputs} = CartForm.getFormInput(formData);

@@ -69,6 +73,8 @@ export async function action({request, context}: Route.ActionArgs) {
@@ -74,6 +78,8 @@ export async function action({request, context}: Route.ActionArgs) {
case CartForm.ACTIONS.BuyerIdentityUpdate: {
result = await cart.updateBuyerIdentity({
...inputs.buyerIdentity,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
index a9b5b4dd3..f743d0edb 100644
index f82d683fd..f08a6777e 100644
--- a/templates/skeleton/app/routes/cart.tsx
+++ b/templates/skeleton/app/routes/cart.tsx
@@ -6,6 +6,10 @@ import {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
index dc4426a9f..5300adaf6 100644
index cf35c25ea..cf79917eb 100644
--- a/templates/skeleton/app/lib/fragments.ts
+++ b/templates/skeleton/app/lib/fragments.ts
@@ -47,6 +47,11 @@ export const CART_QUERY_FRAGMENT = `#graphql
Expand All @@ -25,7 +25,7 @@ index dc4426a9f..5300adaf6 100644
}
selectedOptions {
name
@@ -231,3 +241,23 @@ export const FOOTER_QUERY = `#graphql
@@ -232,3 +242,23 @@ export const FOOTER_QUERY = `#graphql
}
${MENU_FRAGMENT}
` as const;
Expand Down
18 changes: 9 additions & 9 deletions cookbook/recipes/custom-cart-method/recipe.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,35 +30,35 @@ steps:
description: Updates README with custom cart method documentation and implementation guide
diffs:
- file: README.md
patchFile: README.md.47d06f.patch
patchFile: README.md.49f8fa.patch
- type: PATCH
step: '1'
name: app/components/CartLineItem.tsx
description: null
description: Adds variant selector functionality to cart line items for changing product options
diffs:
- file: app/components/CartLineItem.tsx
patchFile: CartLineItem.tsx.12f63e.patch
patchFile: CartLineItem.tsx.f93ff4.patch
- type: PATCH
step: '2'
name: app/lib/context.ts
description: null
description: Extends HydrogenCart context with updateLineByOptions method for variant switching
diffs:
- file: app/lib/context.ts
patchFile: context.ts.276f49.patch
patchFile: context.ts.cfd9da.patch
- type: PATCH
step: '3'
name: app/lib/fragments.ts
description: null
description: Adds product options to cart fragments and creates PRODUCT_VARIANT_QUERY for fetching variants
diffs:
- file: app/lib/fragments.ts
patchFile: fragments.ts.5f86c7.patch
patchFile: fragments.ts.18726b.patch
- type: PATCH
step: '4'
name: app/routes/cart.tsx
description: null
description: Implements CustomUpdateLineByOptions action handler for processing variant changes in cart
diffs:
- file: app/routes/cart.tsx
patchFile: cart.tsx.362410.patch
patchFile: cart.tsx.943271.patch
nextSteps: null
llms:
userQueries: []
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
index dc4426a9f..76d6a1d9c 100644
index cf35c25ea..99874574c 100644
--- a/templates/skeleton/app/lib/fragments.ts
+++ b/templates/skeleton/app/lib/fragments.ts
@@ -231,3 +231,25 @@ export const FOOTER_QUERY = `#graphql
@@ -232,3 +232,25 @@ export const FOOTER_QUERY = `#graphql
}
${MENU_FRAGMENT}
` as const;
Expand Down
Loading