Skip to content

Commit

Permalink
Add magic cart and discount routes to skeleton template (#1309)
Browse files Browse the repository at this point in the history
* add magic cart link route

* add magic discount code route

* update route map for cart cli generate command

* add changeset

* Update templates/skeleton/app/routes/cart.$lines.tsx

Co-authored-by: Bret Little <[email protected]>

---------

Co-authored-by: Bret Little <[email protected]>
  • Loading branch information
juanpprieto and blittle authored Sep 19, 2023
1 parent d8dc1ac commit 33ae6ab
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .changeset/chatty-bugs-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@shopify/cli-hydrogen': patch
'@shopify/create-hydrogen': patch
---

Add magic cart and discount routes to skeleton template
2 changes: 1 addition & 1 deletion packages/cli/src/lib/setups/routes/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const NO_LOCALE_PATTERNS = [/robots\.txt/];
const ROUTE_MAP = {
home: ['_index', '$'],
page: 'pages*',
cart: 'cart',
cart: ['cart', 'cart.$lines', 'discount.$code'],
products: 'products*',
collections: 'collections*',
policies: 'policies*',
Expand Down
70 changes: 70 additions & 0 deletions templates/skeleton/app/routes/cart.$lines.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {redirect, type LoaderArgs} from '@shopify/remix-oxygen';

/**
* Automatically creates a new cart based on the URL and redirects straight to checkout.
* Expected URL structure:
* ```ts
* /cart/<variant_id>:<quantity>
*
* ```
* More than one `<variant_id>:<quantity>` separated by a comma, can be supplied in the URL, for
* carts with more than one product variant.
*
* @param `?discount` an optional discount code to apply to the cart
* @example
* Example path creating a cart with two product variants, different quantities, and a discount code:
* ```ts
* /cart/41007289663544:1,41007289696312:2?discount=HYDROBOARD
*
* ```
* @preserve
*/
export async function loader({request, context, params}: LoaderArgs) {
const {cart} = context;
const {lines} = params;
if (!lines) return redirect('/cart');
const linesMap = lines.split(',').map((line) => {
const lineDetails = line.split(':');
const variantId = lineDetails[0];
const quantity = parseInt(lineDetails[1], 10);

return {
merchandiseId: `gid://shopify/ProductVariant/${variantId}`,
quantity,
};
});

const url = new URL(request.url);
const searchParams = new URLSearchParams(url.search);

const discount = searchParams.get('discount');
const discountArray = discount ? [discount] : [];

// create a cart
const result = await cart.create({
lines: linesMap,
discountCodes: discountArray,
});

const cartResult = result.cart;

if (result.errors?.length || !cartResult) {
throw new Response('Link may be expired. Try checking the URL.', {
status: 410,
});
}

// Update cart id in cookie
const headers = cart.setCartId(cartResult.id);

// redirect to checkout
if (cartResult.checkoutUrl) {
return redirect(cartResult.checkoutUrl, {headers});
} else {
throw new Error('No checkout URL found');
}
}

export default function Component() {
return null;
}
43 changes: 43 additions & 0 deletions templates/skeleton/app/routes/discount.$code.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {redirect, type LoaderArgs} from '@shopify/remix-oxygen';

/**
* Automatically applies a discount found on the url
* If a cart exists it's updated with the discount, otherwise a cart is created with the discount already applied
* @param ?redirect an optional path to return to otherwise return to the home page
* @example
* Example path applying a discount and redirecting
* ```ts
* /discount/FREESHIPPING?redirect=/products
*
* ```
* @preserve
*/
export async function loader({request, context, params}: LoaderArgs) {
const {cart} = context;
const {code} = params;

const url = new URL(request.url);
const searchParams = new URLSearchParams(url.search);
const redirectParam =
searchParams.get('redirect') || searchParams.get('return_to') || '/';

searchParams.delete('redirect');
searchParams.delete('return_to');

const redirectUrl = `${redirectParam}?${searchParams}`;

if (!code) {
return redirect(redirectUrl);
}

const result = await cart.updateDiscountCodes([code]);
const headers = cart.setCartId(result.cart.id);

// Using set-cookie on a 303 redirect will not work if the domain origin have port number (:3000)
// If there is no cart id and a new cart id is created in the progress, it will not be set in the cookie
// on localhost:3000
return redirect(redirectUrl, {
status: 303,
headers,
});
}

0 comments on commit 33ae6ab

Please sign in to comment.