diff --git a/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts b/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts index bf449cb11..734a64402 100644 --- a/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts +++ b/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts @@ -3,7 +3,6 @@ import { parseWishlistItem } from '../../utils/parse-item' import getCustomerId from '../../utils/get-customer-id' import type { WishlistEndpoint } from '.' -// Return wishlist info const addItem: WishlistEndpoint['handlers']['addItem'] = async ({ res, body: { customerToken, item }, @@ -17,41 +16,52 @@ const addItem: WishlistEndpoint['handlers']['addItem'] = async ({ }) } - const customerId = - customerToken && (await getCustomerId({ customerToken, config })) + try { + const customerId = + customerToken && (await getCustomerId({ customerToken, config })) - if (!customerId) { - return res.status(400).json({ + if (!customerId) { + throw new Error('Invalid request. No CustomerId') + } + + let { wishlist } = await commerce.getCustomerWishlist({ + variables: { customerId }, + config, + }) + + if (!wishlist) { + // If user has no wishlist, then let's create one with new item + const { data } = await config.storeApiFetch('/v3/wishlists', { + method: 'POST', + body: JSON.stringify({ + name: 'Next.js Commerce Wishlist', + is_public: false, + customer_id: Number(customerId), + items: [parseWishlistItem(item)], + }), + }) + return res.status(200).json(data) + } + + // Existing Wishlist, let's add Item to Wishlist + const { data } = await config.storeApiFetch( + `/v3/wishlists/${wishlist.id}/items`, + { + method: 'POST', + body: JSON.stringify({ + items: [parseWishlistItem(item)], + }), + } + ) + + // Returns Wishlist + return res.status(200).json(data) + } catch (err: any) { + res.status(500).json({ data: null, - errors: [{ message: 'Invalid request' }], + errors: [{ message: err.message }], }) } - - const { wishlist } = await commerce.getCustomerWishlist({ - variables: { customerId }, - config, - }) - const options = { - method: 'POST', - body: JSON.stringify( - wishlist - ? { - items: [parseWishlistItem(item)], - } - : { - name: 'Wishlist', - customer_id: customerId, - items: [parseWishlistItem(item)], - is_public: false, - } - ), - } - - const { data } = wishlist - ? await config.storeApiFetch(`/v3/wishlists/${wishlist.id}/items`, options) - : await config.storeApiFetch('/v3/wishlists', options) - - res.status(200).json({ data }) } export default addItem diff --git a/packages/bigcommerce/src/api/index.ts b/packages/bigcommerce/src/api/index.ts index b4999642b..bd5fcb3b7 100644 --- a/packages/bigcommerce/src/api/index.ts +++ b/packages/bigcommerce/src/api/index.ts @@ -38,9 +38,9 @@ export interface BigcommerceConfig extends CommerceAPIConfig { storeApiFetch(endpoint: string, options?: RequestInit): Promise } -const API_URL = process.env.BIGCOMMERCE_STOREFRONT_API_URL +const API_URL = process.env.BIGCOMMERCE_STOREFRONT_API_URL // GraphAPI const API_TOKEN = process.env.BIGCOMMERCE_STOREFRONT_API_TOKEN -const STORE_API_URL = process.env.BIGCOMMERCE_STORE_API_URL +const STORE_API_URL = process.env.BIGCOMMERCE_STORE_API_URL // REST API const STORE_API_TOKEN = process.env.BIGCOMMERCE_STORE_API_TOKEN const STORE_API_CLIENT_ID = process.env.BIGCOMMERCE_STORE_API_CLIENT_ID const STORE_CHANNEL_ID = process.env.BIGCOMMERCE_CHANNEL_ID diff --git a/packages/bigcommerce/src/api/operations/get-customer-wishlist.ts b/packages/bigcommerce/src/api/operations/get-customer-wishlist.ts index dc0dd0d9a..25e2157e1 100644 --- a/packages/bigcommerce/src/api/operations/get-customer-wishlist.ts +++ b/packages/bigcommerce/src/api/operations/get-customer-wishlist.ts @@ -44,6 +44,7 @@ export default function getCustomerWishlistOperation({ const { data = [] } = await config.storeApiFetch< RecursivePartial<{ data: Wishlist[] }> >(`/v3/wishlists?customer_id=${variables.customerId}`) + const wishlist = data[0] if (includeProducts && wishlist?.items?.length) { diff --git a/packages/bigcommerce/src/wishlist/use-wishlist.tsx b/packages/bigcommerce/src/wishlist/use-wishlist.tsx index e77d6885b..7882233f5 100644 --- a/packages/bigcommerce/src/wishlist/use-wishlist.tsx +++ b/packages/bigcommerce/src/wishlist/use-wishlist.tsx @@ -1,11 +1,13 @@ import { useMemo } from 'react' import { SWRHook } from '@vercel/commerce/utils/types' -import useWishlist, { UseWishlist } from '@vercel/commerce/wishlist/use-wishlist' -import type { GetWishlistHook } from '../types/wishlist' +import useWishlist, { + UseWishlist, +} from '@vercel/commerce/wishlist/use-wishlist' import useCustomer from '../customer/use-customer' -export default useWishlist as UseWishlist +import type { GetWishlistHook } from '../types/wishlist' +export default useWishlist as UseWishlist export const handler: SWRHook = { fetchOptions: { url: '/api/wishlist', diff --git a/site/assets/base.css b/site/assets/base.css index 05a234a73..37e14196c 100644 --- a/site/assets/base.css +++ b/site/assets/base.css @@ -4,11 +4,9 @@ --secondary: #000000; --secondary-2: #111; --selection: var(--cyan); - --text-base: #000000; --text-primary: #000000; --text-secondary: white; - --hover: rgba(0, 0, 0, 0.075); --hover-1: rgba(0, 0, 0, 0.15); --hover-2: rgba(0, 0, 0, 0.25); @@ -17,15 +15,11 @@ --red: #da3c3c; --purple: #f81ce5; --blue: #0070f3; - --pink: #ff0080; --pink-light: #ff379c; - --magenta: #eb367f; - --violet: #7928ca; --violet-dark: #4c2889; - --accent-0: #fff; --accent-1: #fafafa; --accent-2: #eaeaea; @@ -36,7 +30,6 @@ --accent-7: #333333; --accent-8: #111111; --accent-9: #000; - --font-sans: -apple-system, system-ui, BlinkMacSystemFont, 'Helvetica Neue', 'Helvetica', sans-serif; } @@ -50,11 +43,9 @@ --hover-1: rgba(255, 255, 255, 0.15); --hover-2: rgba(255, 255, 255, 0.25); --selection: var(--purple); - --text-base: white; --text-primary: white; --text-secondary: black; - --accent-9: #fff; --accent-8: #fafafa; --accent-7: #eaeaea; @@ -73,17 +64,11 @@ box-sizing: inherit; } -html { +html, +body { height: 100%; box-sizing: border-box; touch-action: manipulation; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -html, -body { font-family: var(--font-sans); text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; @@ -104,15 +89,15 @@ a { } .animated { - -webkit-animation-duration: 1s; animation-duration: 1s; - -webkit-animation-fill-mode: both; animation-fill-mode: both; + -webkit-animation-duration: 1s; + -webkit-animation-fill-mode: both; } .fadeIn { - -webkit-animation-name: fadeIn; animation-name: fadeIn; + -webkit-animation-name: fadeIn; } @-webkit-keyframes fadeIn { diff --git a/site/commerce.config.json b/site/commerce.config.json index dd43ae077..ad72b58de 100644 --- a/site/commerce.config.json +++ b/site/commerce.config.json @@ -5,11 +5,5 @@ "wishlist": false, "customerAuth": false, "customCheckout": false - }, - "@vercel/commerce-bigcommerce": { - "features": { - "wishlist": false, - "customerAuth": false - } } } diff --git a/site/components/common/Layout/Layout.tsx b/site/components/common/Layout/Layout.tsx index 5c936a09f..6dda880e4 100644 --- a/site/components/common/Layout/Layout.tsx +++ b/site/components/common/Layout/Layout.tsx @@ -1,12 +1,11 @@ import cn from 'clsx' -import React, { FC } from 'react' +import s from './Layout.module.css' import dynamic from 'next/dynamic' import { useRouter } from 'next/router' import { CommerceProvider } from '@framework' +import LoginView from '@components/auth/LoginView' import { useUI } from '@components/ui/context' -import type { Page } from '@commerce/types/page' import { Navbar, Footer } from '@components/common' -import type { Category } from '@commerce/types/site' import ShippingView from '@components/checkout/ShippingView' import CartSidebarView from '@components/cart/CartSidebarView' import { useAcceptCookies } from '@lib/hooks/useAcceptCookies' @@ -14,10 +13,10 @@ import { Sidebar, Button, LoadingDots } from '@components/ui' import PaymentMethodView from '@components/checkout/PaymentMethodView' import CheckoutSidebarView from '@components/checkout/CheckoutSidebarView' import { CheckoutProvider } from '@components/checkout/context' -import MenuSidebarView, { Link } from '../UserNav/MenuSidebarView' - -import LoginView from '@components/auth/LoginView' -import s from './Layout.module.css' +import { MenuSidebarView } from '@components/common/UserNav' +import type { Page } from '@commerce/types/page' +import type { Category } from '@commerce/types/site' +import type { Link as LinkProps } from '../UserNav/MenuSidebarView' const Loading = () => (
@@ -29,35 +28,26 @@ const dynamicProps = { loading: Loading, } -const SignUpView = dynamic( - () => import('@components/auth/SignUpView'), - { - ...dynamicProps - } -) +const SignUpView = dynamic(() => import('@components/auth/SignUpView'), { + ...dynamicProps, +}) const ForgotPassword = dynamic( () => import('@components/auth/ForgotPassword'), - { - ...dynamicProps - } -) - -const FeatureBar = dynamic( - () => import('@components/common/FeatureBar'), - { - ...dynamicProps - } -) - -const Modal = dynamic( - () => import('@components/ui/Modal'), { ...dynamicProps, - ssr: false } ) +const FeatureBar = dynamic(() => import('@components/common/FeatureBar'), { + ...dynamicProps, +}) + +const Modal = dynamic(() => import('@components/ui/Modal'), { + ...dynamicProps, + ssr: false, +}) + interface Props { pageProps: { pages?: Page[] @@ -65,7 +55,7 @@ interface Props { } } -const ModalView: FC<{ modalView: string; closeModal(): any }> = ({ +const ModalView: React.FC<{ modalView: string; closeModal(): any }> = ({ modalView, closeModal, }) => { @@ -78,41 +68,41 @@ const ModalView: FC<{ modalView: string; closeModal(): any }> = ({ ) } -const ModalUI: FC = () => { +const ModalUI: React.FC = () => { const { displayModal, closeModal, modalView } = useUI() return displayModal ? ( ) : null } -const SidebarView: FC<{ +const SidebarView: React.FC<{ sidebarView: string closeSidebar(): any - links: Link[] + links: LinkProps[] }> = ({ sidebarView, closeSidebar, links }) => { return ( - {sidebarView === 'MOBILEMENU_VIEW' && } {sidebarView === 'CART_VIEW' && } - {sidebarView === 'CHECKOUT_VIEW' && } - {sidebarView === 'PAYMENT_VIEW' && } {sidebarView === 'SHIPPING_VIEW' && } + {sidebarView === 'PAYMENT_VIEW' && } + {sidebarView === 'CHECKOUT_VIEW' && } + {sidebarView === 'MOBILE_MENU_VIEW' && } ) } -const SidebarUI: FC<{ links: any }> = ({ links }) => { +const SidebarUI: React.FC<{ links: LinkProps[] }> = ({ links }) => { const { displaySidebar, closeSidebar, sidebarView } = useUI() return displaySidebar ? ( ) : null } -const Layout: FC = ({ +const Layout: React.FC = ({ children, pageProps: { categories = [], ...pageProps }, }) => { diff --git a/site/components/common/Navbar/Navbar.tsx b/site/components/common/Navbar/Navbar.tsx index bc1969a91..9286ef160 100644 --- a/site/components/common/Navbar/Navbar.tsx +++ b/site/components/common/Navbar/Navbar.tsx @@ -16,7 +16,7 @@ interface NavbarProps { const Navbar: FC = ({ links }) => ( - +
diff --git a/site/components/common/SidebarLayout/SidebarLayout.tsx b/site/components/common/SidebarLayout/SidebarLayout.tsx index 407a938ea..b291e8a68 100644 --- a/site/components/common/SidebarLayout/SidebarLayout.tsx +++ b/site/components/common/SidebarLayout/SidebarLayout.tsx @@ -12,8 +12,8 @@ type ComponentProps = { className?: string } & ( const SidebarLayout: FC = ({ children, className, - handleClose, handleBack, + handleClose, }) => { return (
@@ -38,9 +38,8 @@ const SidebarLayout: FC = ({ Back )} - - - + +
{children}
diff --git a/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.module.css b/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.module.css new file mode 100644 index 000000000..93a183a2b --- /dev/null +++ b/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.module.css @@ -0,0 +1,31 @@ +.root { + @apply inset-0 fixed; + left: 72px; + z-index: 10; + height: 100vh; + min-width: 100vw; + transition: none; +} + +@media screen(lg) { + .root { + @apply static; + min-width: inherit; + height: inherit; + } +} + +.link { + @apply text-primary flex cursor-pointer px-6 py-3 + transition ease-in-out duration-150 leading-6 + font-medium items-center capitalize w-full box-border + outline-0; +} + +.link:hover { + @apply bg-accent-1 outline-none; +} + +.link.active { + @apply font-bold bg-accent-2; +} diff --git a/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.tsx b/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.tsx new file mode 100644 index 000000000..992d17178 --- /dev/null +++ b/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.tsx @@ -0,0 +1,86 @@ +import cn from 'clsx' +import { useTheme } from 'next-themes' +import { useRouter } from 'next/router' +import { Moon, Sun } from '@components/icons' +import s from './CustomerMenuContent.module.css' +import useLogout from '@framework/auth/use-logout' +import { + DropdownContent, + DropdownMenuItem, +} from '@components/ui/Dropdown/Dropdown' + +const LINKS = [ + { + name: 'My Orders', + href: '/orders', + }, + { + name: 'My Profile', + href: '/profile', + }, + { + name: 'My Cart', + href: '/cart', + }, +] + +export default function CustomerMenuContent() { + const router = useRouter() + const logout = useLogout() + const { pathname } = useRouter() + const { theme, setTheme } = useTheme() + + function handleClick(_: React.MouseEvent, href: string) { + router.push(href) + } + + return ( + + {LINKS.map(({ name, href }) => ( + + handleClick(e, href)} + > + {name} + + + ))} + + { + setTheme(theme === 'dark' ? 'light' : 'dark') + }} + > +
+ Theme: {theme}{' '} +
+
+ {theme == 'dark' ? ( + + ) : ( + + )} +
+
+
+ + logout()} + > + Logout + + +
+ ) +} diff --git a/site/components/common/UserNav/CustomerMenuContent/index.ts b/site/components/common/UserNav/CustomerMenuContent/index.ts new file mode 100644 index 000000000..b465e81d6 --- /dev/null +++ b/site/components/common/UserNav/CustomerMenuContent/index.ts @@ -0,0 +1 @@ +export { default } from './CustomerMenuContent' diff --git a/site/components/common/UserNav/DropdownMenu.module.css b/site/components/common/UserNav/DropdownMenu.module.css deleted file mode 100644 index 8c4d9eb98..000000000 --- a/site/components/common/UserNav/DropdownMenu.module.css +++ /dev/null @@ -1,26 +0,0 @@ -@screen lg { - .dropdownMenu { - @apply absolute top-10 border border-accent-1 shadow-lg w-56 h-auto; - } -} - -.dropdownMenu { - @apply fixed right-0 mt-2 origin-top-right outline-none bg-primary z-40 w-full h-full; -} - -.link { - @apply text-primary flex cursor-pointer px-6 py-3 flex transition ease-in-out duration-150 leading-6 font-medium items-center; - text-transform: capitalize; -} - -.link:hover { - @apply bg-accent-1; -} - -.link.active { - @apply font-bold bg-accent-2; -} - -.off { - @apply hidden; -} diff --git a/site/components/common/UserNav/DropdownMenu.tsx b/site/components/common/UserNav/DropdownMenu.tsx deleted file mode 100644 index 9a73003dc..000000000 --- a/site/components/common/UserNav/DropdownMenu.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import cn from 'clsx' -import Link from 'next/link' -import { FC, useRef, useState, useEffect } from 'react' -import { useTheme } from 'next-themes' -import { useRouter } from 'next/router' -import s from './DropdownMenu.module.css' -import { Avatar } from '@components/common' -import { Moon, Sun } from '@components/icons' -import { useUI } from '@components/ui/context' -import ClickOutside from '@lib/click-outside' -import useLogout from '@framework/auth/use-logout' - -import { - disableBodyScroll, - enableBodyScroll, - clearAllBodyScrollLocks, -} from 'body-scroll-lock' - -interface DropdownMenuProps { - open?: boolean -} - -const LINKS = [ - { - name: 'My Orders', - href: '/orders', - }, - { - name: 'My Profile', - href: '/profile', - }, - { - name: 'My Cart', - href: '/cart', - }, -] - -const DropdownMenu: FC = ({ open = false }) => { - const logout = useLogout() - const { pathname } = useRouter() - const { theme, setTheme } = useTheme() - const [display, setDisplay] = useState(false) - const { closeSidebarIfPresent } = useUI() - const ref = useRef() as React.MutableRefObject - - useEffect(() => { - if (ref.current) { - if (display) { - disableBodyScroll(ref.current) - } else { - enableBodyScroll(ref.current) - } - } - return () => { - clearAllBodyScrollLocks() - } - }, [display]) - - return ( - setDisplay(false)}> - - - ) -} - -export default DropdownMenu diff --git a/site/components/common/UserNav/MenuSidebarView/MenuSidebarView.module.css b/site/components/common/UserNav/MenuSidebarView/MenuSidebarView.module.css index 26469d6b6..6c05b013e 100644 --- a/site/components/common/UserNav/MenuSidebarView/MenuSidebarView.module.css +++ b/site/components/common/UserNav/MenuSidebarView/MenuSidebarView.module.css @@ -3,5 +3,5 @@ } .item { - @apply text-2xl font-bold; -} \ No newline at end of file + @apply text-xl font-bold py-2; +} diff --git a/site/components/common/UserNav/MenuSidebarView/MenuSidebarView.tsx b/site/components/common/UserNav/MenuSidebarView/MenuSidebarView.tsx index 334b8de9e..a4ed5b51b 100644 --- a/site/components/common/UserNav/MenuSidebarView/MenuSidebarView.tsx +++ b/site/components/common/UserNav/MenuSidebarView/MenuSidebarView.tsx @@ -1,31 +1,32 @@ import Link from 'next/link' import s from './MenuSidebarView.module.css' -import { FC } from 'react' import { useUI } from '@components/ui/context' import SidebarLayout from '@components/common/SidebarLayout' -import { Link as LinkProps} from '.' +import type { Link as LinkProps } from './index' - -interface MenuProps { +export default function MenuSidebarView({ + links = [], +}: { links?: LinkProps[] -} - -const MenuSidebarView: FC = (props) => { +}) { const { closeSidebar } = useUI() - const handleClose = () => closeSidebar() return ( - + closeSidebar()}>