diff --git a/README.md b/README.md index 8a8fa68a69..04b9779366 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ Looking for Next.js Commerce v1? [View the release notes](https://github.com/ver 2. Link local instance with Vercel and Github accounts (creates .vercel file): `vercel link` 3. Download your environment variables: `vercel env pull .env.local` - ```bash pnpm install pnpm dev diff --git a/components/cart/delete-item-button.tsx b/components/cart/delete-item-button.tsx index 3a162f5884..789a87754c 100644 --- a/components/cart/delete-item-button.tsx +++ b/components/cart/delete-item-button.tsx @@ -3,6 +3,7 @@ import LoadingDots from 'components/loading-dots'; import { useRouter } from 'next/navigation'; import { startTransition, useState } from 'react'; +import clsx from 'clsx'; import type { CartItem } from 'lib/shopify/types'; export default function DeleteItemButton({ item }: { item: CartItem }) { @@ -36,14 +37,17 @@ export default function DeleteItemButton({ item }: { item: CartItem }) { aria-label="Remove cart item" onClick={handleRemove} disabled={removing} - className={`${ - removing ? 'cursor-not-allowed' : '' - } mr-2 flex h-8 w-8 items-center justify-center border border-black/40 bg-black/0 hover:bg-black/10 dark:border-white/40 dark:bg-white/0 dark:hover:bg-white/10`} + className={clsx( + 'ease flex min-w-[36px] max-w-[36px] items-center justify-center border px-2 transition-all duration-200 hover:border-gray-800 hover:bg-gray-100 dark:border-gray-700 dark:hover:border-gray-600 dark:hover:bg-gray-900', + { + 'cursor-not-allowed px-0': removing + } + )} > {removing ? ( - + ) : ( - + )} ); diff --git a/components/cart/edit-item-quantity-button.tsx b/components/cart/edit-item-quantity-button.tsx index 3dac3e5aa0..2249bd1aa7 100644 --- a/components/cart/edit-item-quantity-button.tsx +++ b/components/cart/edit-item-quantity-button.tsx @@ -1,6 +1,7 @@ import { useRouter } from 'next/navigation'; import { startTransition, useState } from 'react'; +import clsx from 'clsx'; import MinusIcon from 'components/icons/minus'; import PlusIcon from 'components/icons/plus'; import type { CartItem } from 'lib/shopify/types'; @@ -46,16 +47,20 @@ export default function EditItemQuantityButton({ aria-label={type === 'plus' ? 'Increase item quantity' : 'Reduce item quantity'} onClick={handleEdit} disabled={editing} - className={`${editing ? 'cursor-not-allowed' : ''} ${ - type === 'minus' ? 'ml-auto' : '' - } flex h-8 w-8 items-center justify-center border-l border-black/40 bg-black/0 hover:bg-black/10 dark:border-white/40 dark:bg-white/0 dark:hover:bg-white/10`} + className={clsx( + 'ease flex min-w-[36px] max-w-[36px] items-center justify-center border px-2 transition-all duration-200 hover:border-gray-800 hover:bg-gray-100 dark:border-gray-700 dark:hover:border-gray-600 dark:hover:bg-gray-900', + { + 'cursor-not-allowed': editing, + 'ml-auto': type === 'minus' + } + )} > {editing ? ( - + ) : type === 'plus' ? ( - + ) : ( - + )} ); diff --git a/components/cart/modal.tsx b/components/cart/modal.tsx index 3a03ebd683..4220cfe94c 100644 --- a/components/cart/modal.tsx +++ b/components/cart/modal.tsx @@ -1,15 +1,21 @@ import { Dialog } from '@headlessui/react'; import { AnimatePresence, motion } from 'framer-motion'; import Image from 'next/image'; +import Link from 'next/link'; import CloseIcon from 'components/icons/close'; import ShoppingBagIcon from 'components/icons/shopping-bag'; import Price from 'components/price'; import { DEFAULT_OPTION } from 'lib/constants'; import type { Cart } from 'lib/shopify/types'; +import { createUrl } from 'lib/utils'; import DeleteItemButton from './delete-item-button'; import EditItemQuantityButton from './edit-item-quantity-button'; +type MerchandiseSearchParams = { + [key: string]: string; +}; + export default function CartModal({ isOpen, onClose, @@ -50,7 +56,7 @@ export default function CartModal({ closed: { translateX: '100%' } }} transition={{ type: 'spring', bounce: 0, duration: 0.3 }} - className="flex w-full flex-col bg-white p-8 text-black dark:bg-black dark:text-white md:w-1/3 lg:w-[30%] lg:px-6" + className="flex w-full flex-col bg-white p-8 text-black dark:bg-black dark:text-white md:w-3/5 lg:w-2/5" >

My Cart

@@ -74,95 +80,102 @@ export default function CartModal({
    {cart.lines.map((item, i) => { + const merchandiseSearchParams = {} as MerchandiseSearchParams; + + item.merchandise.selectedOptions.forEach(({ name, value }) => { + if (value !== DEFAULT_OPTION) { + merchandiseSearchParams[name.toLowerCase()] = value; + } + }); + + const merchandiseUrl = createUrl( + `/product/${item.merchandise.product.handle}`, + new URLSearchParams(merchandiseSearchParams) + ); + return (
  • -
    -
    - {item.merchandise.product.featuredImage.url && ( - { - )} + +
    + {
    -
    -
    -
    -

    - {item.merchandise.product.title} -

    - {item.merchandise.title !== DEFAULT_OPTION ? ( -

    - {item.merchandise.title} -

    - ) : null} -
    - -
    +
    + + {item.merchandise.product.title} + + {item.merchandise.title !== DEFAULT_OPTION ? ( +

    + {item.merchandise.title} +

    + ) : null}
    -
    -
    + + +
    -
    -
    {item.quantity}
    - - -
    +

    + {item.quantity} +

    + +
  • ); })}
-
-
-
-

Subtotal

- -
-
-

Taxes

- -
-
-

Shipping

-

calculated at checkout

-
-
-

Total

- -
+
+
+

Subtotal

+ +
+
+

Taxes

+ +
+
+

Shipping

+

Calculated at checkout

+
+
+

Total

+
- - Proceed to Checkout -
+ + Proceed to Checkout +
) : null} diff --git a/lib/shopify/fragments/cart.ts b/lib/shopify/fragments/cart.ts index c072160c71..fc5c838ddb 100644 --- a/lib/shopify/fragments/cart.ts +++ b/lib/shopify/fragments/cart.ts @@ -33,6 +33,10 @@ const cartFragment = /* GraphQL */ ` ... on ProductVariant { id title + selectedOptions { + name + value + } product { ...product } diff --git a/lib/shopify/index.ts b/lib/shopify/index.ts index 49655aa9e7..c362ea3320 100644 --- a/lib/shopify/index.ts +++ b/lib/shopify/index.ts @@ -44,8 +44,8 @@ import { ShopifyUpdateCartOperation } from './types'; -const domain = process.env.SHOPIFY_STORE_DOMAIN!; -const endpoint = process.env.SHOPIFY_STORE_DOMAIN! + SHOPIFY_GRAPHQL_API_ENDPOINT; +const domain = `https://${process.env.SHOPIFY_STORE_DOMAIN!}`; +const endpoint = `${domain}${SHOPIFY_GRAPHQL_API_ENDPOINT}`; const key = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN!; type ExtractVariables = T extends { variables: object } ? T['variables'] : never; diff --git a/lib/shopify/types.ts b/lib/shopify/types.ts index e0e7d27d8c..d4e7a6c4d6 100644 --- a/lib/shopify/types.ts +++ b/lib/shopify/types.ts @@ -21,6 +21,10 @@ export type CartItem = { merchandise: { id: string; title: string; + selectedOptions: { + name: string; + value: string; + }[]; product: Product; }; }; diff --git a/package.json b/package.json index 7f89571365..cb1c9a9b89 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "clsx": "^1.2.1", "framer-motion": "^8.4.0", "is-empty-iterable": "^3.0.0", - "next": "13.3.1-canary.7", + "next": "13.3.1-canary.13", "react": "18.2.0", "react-cookie": "^4.1.1", "react-dom": "18.2.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 83fdc9d446..7f1e678bad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,8 +17,8 @@ dependencies: specifier: ^3.0.0 version: 3.0.0 next: - specifier: 13.3.1-canary.7 - version: 13.3.1-canary.7(react-dom@18.2.0)(react@18.2.0) + specifier: 13.3.1-canary.13 + version: 13.3.1-canary.13(react-dom@18.2.0)(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -236,8 +236,8 @@ packages: tslib: 2.5.0 dev: false - /@next/env@13.3.1-canary.7: - resolution: {integrity: sha512-Pp0TXXRIVkMekE/vdrwfLEJSI8eKEldB1vlDBQFVytTQHrl474ggHLjVG4rUJzDn2rBg7Xcmf3lX8lunltb3dQ==} + /@next/env@13.3.1-canary.13: + resolution: {integrity: sha512-Zuwdo2KfGQPw0nTizy6yzj/LgtWl5FcDJJ80gJ/1WHJl9ANkuSsmru6EGUoBVkd481A/dfNP60355zfJjqq3Rg==} dev: false /@next/eslint-plugin-next@13.2.1: @@ -246,8 +246,8 @@ packages: glob: 7.1.7 dev: true - /@next/swc-darwin-arm64@13.3.1-canary.7: - resolution: {integrity: sha512-ng2/kMFrgOe7eMM2ibEfmvEY2nqsUh8sAg4lkFtmHEqxiTi3W3pD/eVi5oE5gghz4SxuGIpEVOt14Th/ezRV/A==} + /@next/swc-darwin-arm64@13.3.1-canary.13: + resolution: {integrity: sha512-lwy+zhJnUevo2JxydLUywB3ZWDdQgGGOc5ZWJNxsoef8FVJ3PX8zvZYCWDsD5sGE1BViqjMsbIx/uf4TqaZOhg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -255,8 +255,8 @@ packages: dev: false optional: true - /@next/swc-darwin-x64@13.3.1-canary.7: - resolution: {integrity: sha512-r14lwsA2TZH0PwOpCS0t/el/2TlBT6Cm9FmSLINUW0GTJzJAxlf7zEYEvgPpVMFqdxpsBrKrjGPn6DGm3CO3Ug==} + /@next/swc-darwin-x64@13.3.1-canary.13: + resolution: {integrity: sha512-JvwWrWF4Uqm4qEWLQV5Qt96kW1hmlV+8rjJTTOWll6ebCQz9c7/Exv4kCOz0mBkENYRmQlz3Pgd5ZWaooR4ptQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -264,8 +264,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-gnu@13.3.1-canary.7: - resolution: {integrity: sha512-C7GEXjH7QtJDP4mcWMGQNMpJc3HsNREH9in40t/9f2hxiWBuOCIevUW/IUAW4WNqL10R5cPFDE+4w3CjBvxbxg==} + /@next/swc-linux-arm64-gnu@13.3.1-canary.13: + resolution: {integrity: sha512-OGHPDSjQw4Sqhzgl/fdgZMPPmCj0CJiqIMudyzrBqV9z59vyreIMBzi6sWsC2I5u8EP0Q2+qUFC5AJ4xqyFMZA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -273,8 +273,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-musl@13.3.1-canary.7: - resolution: {integrity: sha512-PJbGnbQ6oppocJ486QEPThwk2tywUaZeMfT0Ge+ddUYe0HeDRu+fSf9uWIOs7yMEWHYOiC45aEFUQPUbnRiaTQ==} + /@next/swc-linux-arm64-musl@13.3.1-canary.13: + resolution: {integrity: sha512-mvPIWB1WLpyCzZOPgHEPkEtZEdyW6U9VHN8HTmyzybqZx/Lo4AWHobXlujWflBTyHQOu3ft9kfS4TAhdB9XFyg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -282,8 +282,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-gnu@13.3.1-canary.7: - resolution: {integrity: sha512-pexbimR0krKixx28qrxEuahr9Lqj8ZdhRawegjHa9YFTh4twswrbJpTldgzb/JFzEmi4jJPJZ6akPdiKghJM2A==} + /@next/swc-linux-x64-gnu@13.3.1-canary.13: + resolution: {integrity: sha512-hTHNN3n94qQcx0YK6bS2/nw99OSZZa/v7s56OOzCfQpYAz2546wiCOoEcOxQy/1Mum23i2Lt3u6km96GyHrD0Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -291,8 +291,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-musl@13.3.1-canary.7: - resolution: {integrity: sha512-MT48RkAT6f1cGzCoLoHnhkk65SC+ugSnzzXn8JxHl8A2H8G3X+RSxmyhwecJKxyGN8p3LCVEtdPeQr7Qut/Hyw==} + /@next/swc-linux-x64-musl@13.3.1-canary.13: + resolution: {integrity: sha512-SW3YdJfJzlin5hp9zMU4HHKKhARq9ojQ1tjUSsgfPtMTG3Gon7dswK/Ap5aguPvqJQK42YaPg7zPr91Ez4piGw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -300,8 +300,8 @@ packages: dev: false optional: true - /@next/swc-win32-arm64-msvc@13.3.1-canary.7: - resolution: {integrity: sha512-8ifclpg13PucmUp5DEcgdKNuwAAnN9FgsIse0SeX4CrSlpUPkl+VkoA0gxeIzW229EZaFEz6ypZxTvgboAwOBA==} + /@next/swc-win32-arm64-msvc@13.3.1-canary.13: + resolution: {integrity: sha512-SHb96TxxtQXJ87DbZVC22mS2jcQfKeDVcrSB5G972JRK/QaUnRrIn/Fr4/OMVgz8wkMDYyJwhVMNPZ1d7FzlQg==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -309,8 +309,8 @@ packages: dev: false optional: true - /@next/swc-win32-ia32-msvc@13.3.1-canary.7: - resolution: {integrity: sha512-y5zFC4Lyfxu7elVptzvZkaxlzJNvBwrqlTzeHFWEbZKIdRXg4DGVPL9CdXWM6gvy+KacAOFoUie4zjRQxODUkw==} + /@next/swc-win32-ia32-msvc@13.3.1-canary.13: + resolution: {integrity: sha512-+TM2In/8yh6Ze4ADs9HKSg2mIjzB7SLz9i/8VUCY3SdVMKpKCtMHYzlo5BkIOSmtW0QsGVEY38+6WLOpf8lXhQ==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -318,8 +318,8 @@ packages: dev: false optional: true - /@next/swc-win32-x64-msvc@13.3.1-canary.7: - resolution: {integrity: sha512-A1QqMd7U7gU89++VBZE5snq8E9/Hj13HTXiP3O447E+mjHeqruxLImycilqu0UyENIoFdmxZTYopOzY5UW2GRw==} + /@next/swc-win32-x64-msvc@13.3.1-canary.13: + resolution: {integrity: sha512-IFnEsOJIvaC/MytzEsTaWcILy2lzuwFi0aAq0NturKFRH/ykjOnLHd98jBz5D8eCPX9QN2oyB57D6i1wbIAJHA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -389,8 +389,8 @@ packages: string.prototype.codepointat: 0.2.1 dev: false - /@swc/helpers@0.4.14: - resolution: {integrity: sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==} + /@swc/helpers@0.5.0: + resolution: {integrity: sha512-SjY/p4MmECVVEWspzSRpQEM3sjR17sP8PbGxELWrT+YZMBfiUyt1MRUNjMV23zohwlG2HYtCQOsCwsTHguXkyg==} dependencies: tslib: 2.5.0 dev: false @@ -2317,8 +2317,8 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /next@13.3.1-canary.7(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-4Tdg73PJeQrM5ktrOjEhgwIy/3t8DWUOppfPGZXPVz4Rvb78OQU6sPJrlVwT2x5hi4jdR9T9UMtjpwzH5IMfmQ==} + /next@13.3.1-canary.13(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-nqLvbeAbJiVFWHzfvUM6D5L/nwjUD7+fc0FWPdJc/jLbInKNqQQHc4+0NKNi8tj6Af9cn4MBPrrg/lyMbf0hVA==} engines: {node: '>=14.6.0'} hasBin: true peerDependencies: @@ -2338,8 +2338,8 @@ packages: sass: optional: true dependencies: - '@next/env': 13.3.1-canary.7 - '@swc/helpers': 0.4.14 + '@next/env': 13.3.1-canary.13 + '@swc/helpers': 0.5.0 busboy: 1.6.0 caniuse-lite: 1.0.30001441 postcss: 8.4.14 @@ -2347,15 +2347,15 @@ packages: react-dom: 18.2.0(react@18.2.0) styled-jsx: 5.1.1(react@18.2.0) optionalDependencies: - '@next/swc-darwin-arm64': 13.3.1-canary.7 - '@next/swc-darwin-x64': 13.3.1-canary.7 - '@next/swc-linux-arm64-gnu': 13.3.1-canary.7 - '@next/swc-linux-arm64-musl': 13.3.1-canary.7 - '@next/swc-linux-x64-gnu': 13.3.1-canary.7 - '@next/swc-linux-x64-musl': 13.3.1-canary.7 - '@next/swc-win32-arm64-msvc': 13.3.1-canary.7 - '@next/swc-win32-ia32-msvc': 13.3.1-canary.7 - '@next/swc-win32-x64-msvc': 13.3.1-canary.7 + '@next/swc-darwin-arm64': 13.3.1-canary.13 + '@next/swc-darwin-x64': 13.3.1-canary.13 + '@next/swc-linux-arm64-gnu': 13.3.1-canary.13 + '@next/swc-linux-arm64-musl': 13.3.1-canary.13 + '@next/swc-linux-x64-gnu': 13.3.1-canary.13 + '@next/swc-linux-x64-musl': 13.3.1-canary.13 + '@next/swc-win32-arm64-msvc': 13.3.1-canary.13 + '@next/swc-win32-ia32-msvc': 13.3.1-canary.13 + '@next/swc-win32-x64-msvc': 13.3.1-canary.13 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros