forked from crowetic/commerce
Merge branch 'agnostic' of https://github.com/vercel/commerce into agnostic
This commit is contained in:
commit
ce2a410cb3
@ -3,3 +3,5 @@ BIGCOMMERCE_STOREFRONT_API_TOKEN=
|
|||||||
BIGCOMMERCE_STORE_API_URL=
|
BIGCOMMERCE_STORE_API_URL=
|
||||||
BIGCOMMERCE_STORE_API_TOKEN=
|
BIGCOMMERCE_STORE_API_TOKEN=
|
||||||
BIGCOMMERCE_STORE_API_CLIENT_ID=
|
BIGCOMMERCE_STORE_API_CLIENT_ID=
|
||||||
|
SHOPIFY_STORE_DOMAIN=
|
||||||
|
SHOPIFY_STOREFRONT_ACCESS_TOKEN=
|
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["esbenp.prettier-vscode"]
|
||||||
|
}
|
@ -1,4 +0,0 @@
|
|||||||
## Changelog
|
|
||||||
|
|
||||||
- Select Variants Working
|
|
||||||
- Click on cart item title, closes the sidebar
|
|
@ -57,18 +57,21 @@ Main folder and its exposed functions
|
|||||||
- getAllProducts
|
- getAllProducts
|
||||||
- `wishlist`
|
- `wishlist`
|
||||||
- useWishlist
|
- useWishlist
|
||||||
- addWishlistItem
|
- useAddItem
|
||||||
- removeWishlistItem
|
- useRemoveItem
|
||||||
- `auth`
|
- `auth`
|
||||||
- useLogin
|
- useLogin
|
||||||
- useLogout
|
- useLogout
|
||||||
- useSignup
|
- useSignup
|
||||||
|
- `customer`
|
||||||
|
- useCustomer
|
||||||
|
- getCustomerId
|
||||||
|
- getCustomerWistlist
|
||||||
- `cart`
|
- `cart`
|
||||||
|
|
||||||
- useCart
|
- useCart
|
||||||
- useAddItem
|
- useAddItem
|
||||||
- useRemoveItem
|
- useRemoveItem
|
||||||
- useCartActions
|
|
||||||
- useUpdateItem
|
- useUpdateItem
|
||||||
|
|
||||||
- `config.json`
|
- `config.json`
|
||||||
|
@ -9,7 +9,7 @@ import usePrice from '@framework/product/use-price'
|
|||||||
import CartItem from '../CartItem'
|
import CartItem from '../CartItem'
|
||||||
import s from './CartSidebarView.module.css'
|
import s from './CartSidebarView.module.css'
|
||||||
|
|
||||||
const CartSidebarView: FC<{ wishlist?: boolean }> = ({ wishlist }) => {
|
const CartSidebarView: FC = () => {
|
||||||
const { closeSidebar } = useUI()
|
const { closeSidebar } = useUI()
|
||||||
const { data, isLoading, isEmpty } = useCart()
|
const { data, isLoading, isEmpty } = useCart()
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ const CartSidebarView: FC<{ wishlist?: boolean }> = ({ wishlist }) => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<UserNav wishlist={wishlist} />
|
<UserNav />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
@ -5,20 +5,17 @@ import { Grid } from '@components/ui'
|
|||||||
import { ProductCard } from '@components/product'
|
import { ProductCard } from '@components/product'
|
||||||
import s from './HomeAllProductsGrid.module.css'
|
import s from './HomeAllProductsGrid.module.css'
|
||||||
import { getCategoryPath, getDesignerPath } from '@lib/search'
|
import { getCategoryPath, getDesignerPath } from '@lib/search'
|
||||||
import wishlist from '@framework/api/wishlist'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
categories?: any
|
categories?: any
|
||||||
brands?: any
|
brands?: any
|
||||||
products?: Product[]
|
products?: Product[]
|
||||||
wishlist?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const HomeAllProductsGrid: FC<Props> = ({
|
const HomeAllProductsGrid: FC<Props> = ({
|
||||||
categories,
|
categories,
|
||||||
brands,
|
brands,
|
||||||
products = [],
|
products = [],
|
||||||
wishlist = false,
|
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={s.root}>
|
<div className={s.root}>
|
||||||
@ -65,7 +62,6 @@ const HomeAllProductsGrid: FC<Props> = ({
|
|||||||
width: 480,
|
width: 480,
|
||||||
height: 480,
|
height: 480,
|
||||||
}}
|
}}
|
||||||
wishlist={wishlist}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -58,11 +58,10 @@ const Layout: FC<Props> = ({
|
|||||||
} = useUI()
|
} = useUI()
|
||||||
const { acceptedCookies, onAcceptCookies } = useAcceptCookies()
|
const { acceptedCookies, onAcceptCookies } = useAcceptCookies()
|
||||||
const { locale = 'en-US' } = useRouter()
|
const { locale = 'en-US' } = useRouter()
|
||||||
const isWishlistEnabled = commerceFeatures?.wishlist
|
|
||||||
return (
|
return (
|
||||||
<CommerceProvider locale={locale}>
|
<CommerceProvider locale={locale}>
|
||||||
<div className={cn(s.root)}>
|
<div className={cn(s.root)}>
|
||||||
<Navbar wishlist={isWishlistEnabled} />
|
<Navbar />
|
||||||
<main className="fit">{children}</main>
|
<main className="fit">{children}</main>
|
||||||
<Footer pages={pageProps.pages} />
|
<Footer pages={pageProps.pages} />
|
||||||
|
|
||||||
@ -73,7 +72,7 @@ const Layout: FC<Props> = ({
|
|||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Sidebar open={displaySidebar} onClose={closeSidebar}>
|
<Sidebar open={displaySidebar} onClose={closeSidebar}>
|
||||||
<CartSidebarView wishlist={isWishlistEnabled} />
|
<CartSidebarView />
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
|
|
||||||
<FeatureBar
|
<FeatureBar
|
||||||
|
@ -5,7 +5,7 @@ import { Searchbar, UserNav } from '@components/common'
|
|||||||
import NavbarRoot from './NavbarRoot'
|
import NavbarRoot from './NavbarRoot'
|
||||||
import s from './Navbar.module.css'
|
import s from './Navbar.module.css'
|
||||||
|
|
||||||
const Navbar: FC<{ wishlist?: boolean }> = ({ wishlist }) => (
|
const Navbar: FC = () => (
|
||||||
<NavbarRoot>
|
<NavbarRoot>
|
||||||
<Container>
|
<Container>
|
||||||
<div className="relative flex flex-row justify-between py-4 align-center md:py-6">
|
<div className="relative flex flex-row justify-between py-4 align-center md:py-6">
|
||||||
@ -36,7 +36,7 @@ const Navbar: FC<{ wishlist?: boolean }> = ({ wishlist }) => (
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end flex-1 space-x-8">
|
<div className="flex justify-end flex-1 space-x-8">
|
||||||
<UserNav wishlist={wishlist} />
|
<UserNav />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -4,20 +4,19 @@ import cn from 'classnames'
|
|||||||
import type { LineItem } from '@framework/types'
|
import type { LineItem } from '@framework/types'
|
||||||
import useCart from '@framework/cart/use-cart'
|
import useCart from '@framework/cart/use-cart'
|
||||||
import useCustomer from '@framework/customer/use-customer'
|
import useCustomer from '@framework/customer/use-customer'
|
||||||
|
import { Avatar } from '@components/common'
|
||||||
import { Heart, Bag } from '@components/icons'
|
import { Heart, Bag } from '@components/icons'
|
||||||
import { useUI } from '@components/ui/context'
|
import { useUI } from '@components/ui/context'
|
||||||
import DropdownMenu from './DropdownMenu'
|
import DropdownMenu from './DropdownMenu'
|
||||||
import s from './UserNav.module.css'
|
import s from './UserNav.module.css'
|
||||||
import { Avatar } from '@components/common'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: string
|
className?: string
|
||||||
wishlist?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const countItem = (count: number, item: LineItem) => count + item.quantity
|
const countItem = (count: number, item: LineItem) => count + item.quantity
|
||||||
|
|
||||||
const UserNav: FC<Props> = ({ className, wishlist = false }) => {
|
const UserNav: FC<Props> = ({ className }) => {
|
||||||
const { data } = useCart()
|
const { data } = useCart()
|
||||||
const { data: customer } = useCustomer()
|
const { data: customer } = useCustomer()
|
||||||
const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI()
|
const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI()
|
||||||
@ -31,7 +30,7 @@ const UserNav: FC<Props> = ({ className, wishlist = false }) => {
|
|||||||
<Bag />
|
<Bag />
|
||||||
{itemsCount > 0 && <span className={s.bagCount}>{itemsCount}</span>}
|
{itemsCount > 0 && <span className={s.bagCount}>{itemsCount}</span>}
|
||||||
</li>
|
</li>
|
||||||
{wishlist && (
|
{process.env.COMMERCE_WISHLIST_ENABLED && (
|
||||||
<li className={s.item}>
|
<li className={s.item}>
|
||||||
<Link href="/wishlist">
|
<Link href="/wishlist">
|
||||||
<a onClick={closeSidebarIfPresent} aria-label="Wishlist">
|
<a onClick={closeSidebarIfPresent} aria-label="Wishlist">
|
||||||
|
@ -11,7 +11,6 @@ interface Props {
|
|||||||
product: Product
|
product: Product
|
||||||
variant?: 'slim' | 'simple'
|
variant?: 'slim' | 'simple'
|
||||||
imgProps?: Omit<ImageProps, 'src'>
|
imgProps?: Omit<ImageProps, 'src'>
|
||||||
wishlist?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const placeholderImg = '/product-img-placeholder.svg'
|
const placeholderImg = '/product-img-placeholder.svg'
|
||||||
@ -21,7 +20,6 @@ const ProductCard: FC<Props> = ({
|
|||||||
product,
|
product,
|
||||||
variant,
|
variant,
|
||||||
imgProps,
|
imgProps,
|
||||||
wishlist = false,
|
|
||||||
...props
|
...props
|
||||||
}) => (
|
}) => (
|
||||||
<Link href={`/product/${product.slug}`} {...props}>
|
<Link href={`/product/${product.slug}`} {...props}>
|
||||||
@ -59,11 +57,11 @@ const ProductCard: FC<Props> = ({
|
|||||||
{product.price.currencyCode}
|
{product.price.currencyCode}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{wishlist && (
|
{process.env.COMMERCE_WISHLIST_ENABLED && (
|
||||||
<WishlistButton
|
<WishlistButton
|
||||||
className={s.wishlistButton}
|
className={s.wishlistButton}
|
||||||
productId={product.id}
|
productId={product.id}
|
||||||
variant={product.variants[0]}
|
variant={product.variants[0] as any}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,9 +4,8 @@ import { NextSeo } from 'next-seo'
|
|||||||
import { FC, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import s from './ProductView.module.css'
|
import s from './ProductView.module.css'
|
||||||
|
|
||||||
import { useUI } from '@components/ui'
|
|
||||||
import { Swatch, ProductSlider } from '@components/product'
|
import { Swatch, ProductSlider } from '@components/product'
|
||||||
import { Button, Container, Text } from '@components/ui'
|
import { Button, Container, Text, useUI } from '@components/ui'
|
||||||
|
|
||||||
import type { Product } from '@commerce/types'
|
import type { Product } from '@commerce/types'
|
||||||
import usePrice from '@framework/product/use-price'
|
import usePrice from '@framework/product/use-price'
|
||||||
@ -19,10 +18,9 @@ interface Props {
|
|||||||
className?: string
|
className?: string
|
||||||
children?: any
|
children?: any
|
||||||
product: Product
|
product: Product
|
||||||
wishlist?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProductView: FC<Props> = ({ product, wishlist = false }) => {
|
const ProductView: FC<Props> = ({ product }) => {
|
||||||
const addItem = useAddItem()
|
const addItem = useAddItem()
|
||||||
const { price } = usePrice({
|
const { price } = usePrice({
|
||||||
amount: product.price.value,
|
amount: product.price.value,
|
||||||
@ -101,7 +99,6 @@ const ProductView: FC<Props> = ({ product, wishlist = false }) => {
|
|||||||
</ProductSlider>
|
</ProductSlider>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={s.sidebar}>
|
<div className={s.sidebar}>
|
||||||
<section>
|
<section>
|
||||||
{product.options?.map((opt) => (
|
{product.options?.map((opt) => (
|
||||||
@ -152,11 +149,11 @@ const ProductView: FC<Props> = ({ product, wishlist = false }) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{wishlist && (
|
{process.env.COMMERCE_WISHLIST_ENABLED && (
|
||||||
<WishlistButton
|
<WishlistButton
|
||||||
className={s.wishlistButton}
|
className={s.wishlistButton}
|
||||||
productId={product.id}
|
productId={product.id}
|
||||||
variant={product.variants[0]!}
|
variant={product.variants[0]! as any}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,6 +8,7 @@ export interface State {
|
|||||||
displayToast: boolean
|
displayToast: boolean
|
||||||
modalView: string
|
modalView: string
|
||||||
toastText: string
|
toastText: string
|
||||||
|
userAvatar: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
@ -17,6 +18,7 @@ const initialState = {
|
|||||||
modalView: 'LOGIN_VIEW',
|
modalView: 'LOGIN_VIEW',
|
||||||
displayToast: false,
|
displayToast: false,
|
||||||
toastText: '',
|
toastText: '',
|
||||||
|
userAvatar: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
type Action =
|
type Action =
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import React, { FC, useState } from 'react'
|
import React, { FC, useState } from 'react'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import { Heart } from '@components/icons'
|
|
||||||
|
|
||||||
import { useUI } from '@components/ui'
|
import { useUI } from '@components/ui'
|
||||||
import type { Product, ProductVariant } from '@commerce/types'
|
import type { Product, ProductVariant } from '@commerce/types'
|
||||||
import useCustomer from '@framework/customer/use-customer'
|
|
||||||
import useAddItem from '@framework/wishlist/use-add-item'
|
import useAddItem from '@framework/wishlist/use-add-item'
|
||||||
|
import useCustomer from '@framework/customer/use-customer'
|
||||||
import useRemoveItem from '@framework/wishlist/use-remove-item'
|
import useRemoveItem from '@framework/wishlist/use-remove-item'
|
||||||
import useWishlist from '@framework/wishlist/use-add-item'
|
import useWishlist from '@framework/wishlist/use-wishlist'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
productId: Product['id']
|
productId: Product['id']
|
||||||
@ -28,7 +27,8 @@ const WishlistButton: FC<Props> = ({
|
|||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
const itemInWishlist = data?.items?.find(
|
const itemInWishlist = data?.items?.find(
|
||||||
(item) => item.product_id === productId && item.variant_id === variant.id
|
(item) =>
|
||||||
|
item.product_id === productId && (item.variant_id as any) === variant.id
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleWishlistChange = async (e: any) => {
|
const handleWishlistChange = async (e: any) => {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import type { MutationHook } from '@commerce/utils/types'
|
import type { MutationHook } from '@commerce/utils/types'
|
||||||
import { CommerceError } from '@commerce/utils/errors'
|
import { CommerceError } from '@commerce/utils/errors'
|
||||||
import useLogin, { UseLogin } from '@commerce/use-login'
|
import useLogin, { UseLogin } from '@commerce/auth/use-login'
|
||||||
import type { LoginBody } from '../api/customers/login'
|
import type { LoginBody } from '../api/customers/login'
|
||||||
import useCustomer from '../customer/use-customer'
|
import useCustomer from '../customer/use-customer'
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import type { MutationHook } from '@commerce/utils/types'
|
import type { MutationHook } from '@commerce/utils/types'
|
||||||
import useLogout, { UseLogout } from '@commerce/use-logout'
|
import useLogout, { UseLogout } from '@commerce/auth/use-logout'
|
||||||
import useCustomer from '../customer/use-customer'
|
import useCustomer from '../customer/use-customer'
|
||||||
|
|
||||||
export default useLogout as UseLogout<typeof handler>
|
export default useLogout as UseLogout<typeof handler>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import type { MutationHook } from '@commerce/utils/types'
|
import type { MutationHook } from '@commerce/utils/types'
|
||||||
import { CommerceError } from '@commerce/utils/errors'
|
import { CommerceError } from '@commerce/utils/errors'
|
||||||
import useSignup, { UseSignup } from '@commerce/use-signup'
|
import useSignup, { UseSignup } from '@commerce/auth/use-signup'
|
||||||
import type { SignupBody } from '../api/customers/signup'
|
import type { SignupBody } from '../api/customers/signup'
|
||||||
import useCustomer from '../customer/use-customer'
|
import useCustomer from '../customer/use-customer'
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"features": {
|
"features": {
|
||||||
"wishlist": false
|
"wishlist": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
37
framework/bigcommerce/next.config.js
Normal file
37
framework/bigcommerce/next.config.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
module.exports = {
|
||||||
|
images: {
|
||||||
|
domains: ['cdn11.bigcommerce.com'],
|
||||||
|
},
|
||||||
|
i18n: {
|
||||||
|
locales: ['en-US', 'es'],
|
||||||
|
defaultLocale: 'en-US',
|
||||||
|
},
|
||||||
|
rewrites() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
source: '/checkout',
|
||||||
|
destination: '/api/bigcommerce/checkout',
|
||||||
|
},
|
||||||
|
// The logout is also an action so this route is not required, but it's also another way
|
||||||
|
// you can allow a logout!
|
||||||
|
{
|
||||||
|
source: '/logout',
|
||||||
|
destination: '/api/bigcommerce/customers/logout?redirect_to=/',
|
||||||
|
},
|
||||||
|
// Rewrites for /search
|
||||||
|
{
|
||||||
|
source: '/search/designers/:name',
|
||||||
|
destination: '/search',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '/search/designers/:name/:category',
|
||||||
|
destination: '/search',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// This rewrite will also handle `/search/designers`
|
||||||
|
source: '/search/:category',
|
||||||
|
destination: '/search',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
@ -1,2 +1,2 @@
|
|||||||
export * from '@commerce/use-price'
|
export * from '@commerce/product/use-price'
|
||||||
export { default } from '@commerce/use-price'
|
export { default } from '@commerce/product/use-price'
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
export { default as useAddItem } from './use-add-item'
|
export { default as useAddItem } from './use-add-item'
|
||||||
export { default as useWishlist } from './use-wishlist'
|
export { default as useWishlist } from './use-wishlist'
|
||||||
export { default as useRemoveItem } from './use-remove-item'
|
export { default as useRemoveItem } from './use-remove-item'
|
||||||
export { default as useWishlistActions } from './use-wishlist-actions'
|
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
import useAddItem from './use-add-item'
|
|
||||||
import useRemoveItem from './use-remove-item'
|
|
||||||
|
|
||||||
// This hook is probably not going to be used, but it's here
|
|
||||||
// to show how a commerce should be structuring it
|
|
||||||
export default function useWishlistActions() {
|
|
||||||
const addItem = useAddItem()
|
|
||||||
const removeItem = useRemoveItem()
|
|
||||||
|
|
||||||
return { addItem, removeItem }
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
import { useHook, useMutationHook } from './utils/use-hook'
|
import { useHook, useMutationHook } from '../utils/use-hook'
|
||||||
import { mutationFetcher } from './utils/default-fetcher'
|
import { mutationFetcher } from '../utils/default-fetcher'
|
||||||
import type { MutationHook, HookFetcherFn } from './utils/types'
|
import type { MutationHook, HookFetcherFn } from '../utils/types'
|
||||||
import type { Provider } from '.'
|
import type { Provider } from '..'
|
||||||
|
|
||||||
export type UseLogin<
|
export type UseLogin<
|
||||||
H extends MutationHook<any, any, any> = MutationHook<null, {}, {}>
|
H extends MutationHook<any, any, any> = MutationHook<null, {}, {}>
|
@ -1,7 +1,7 @@
|
|||||||
import { useHook, useMutationHook } from './utils/use-hook'
|
import { useHook, useMutationHook } from '../utils/use-hook'
|
||||||
import { mutationFetcher } from './utils/default-fetcher'
|
import { mutationFetcher } from '../utils/default-fetcher'
|
||||||
import type { HookFetcherFn, MutationHook } from './utils/types'
|
import type { HookFetcherFn, MutationHook } from '../utils/types'
|
||||||
import type { Provider } from '.'
|
import type { Provider } from '..'
|
||||||
|
|
||||||
export type UseLogout<
|
export type UseLogout<
|
||||||
H extends MutationHook<any, any, any> = MutationHook<null>
|
H extends MutationHook<any, any, any> = MutationHook<null>
|
@ -1,7 +1,7 @@
|
|||||||
import { useHook, useMutationHook } from './utils/use-hook'
|
import { useHook, useMutationHook } from '../utils/use-hook'
|
||||||
import { mutationFetcher } from './utils/default-fetcher'
|
import { mutationFetcher } from '../utils/default-fetcher'
|
||||||
import type { HookFetcherFn, MutationHook } from './utils/types'
|
import type { HookFetcherFn, MutationHook } from '../utils/types'
|
||||||
import type { Provider } from '.'
|
import type { Provider } from '..'
|
||||||
|
|
||||||
export type UseSignup<
|
export type UseSignup<
|
||||||
H extends MutationHook<any, any, any> = MutationHook<null>
|
H extends MutationHook<any, any, any> = MutationHook<null>
|
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"features": {
|
|
||||||
"wishlist": true
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { useCommerce } from '.'
|
import { useCommerce } from '..'
|
||||||
|
|
||||||
export function formatPrice({
|
export function formatPrice({
|
||||||
amount,
|
amount,
|
@ -2,8 +2,10 @@ import type { Wishlist as BCWishlist } from '@framework/api/wishlist'
|
|||||||
import type { Customer as BCCustomer } from '@framework/api/customers'
|
import type { Customer as BCCustomer } from '@framework/api/customers'
|
||||||
import type { SearchProductsData as BCSearchProductsData } from '@framework/api/catalog/products'
|
import type { SearchProductsData as BCSearchProductsData } from '@framework/api/catalog/products'
|
||||||
|
|
||||||
|
export type Features = 'wishlist' | 'checkout' | string
|
||||||
|
|
||||||
export type CommerceProviderConfig = {
|
export type CommerceProviderConfig = {
|
||||||
features: Record<string, boolean>
|
features: Record<Features, boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Discount = {
|
export type Discount = {
|
||||||
|
11
framework/commerce/utils/bootstrap.js
vendored
Normal file
11
framework/commerce/utils/bootstrap.js
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module.exports = ({ features }) => {
|
||||||
|
let output = {
|
||||||
|
env: {},
|
||||||
|
}
|
||||||
|
if (!!Object.keys(features).length) {
|
||||||
|
Object.keys(features).map(
|
||||||
|
(r) => (output.env[`COMMERCE_${r.toUpperCase()}_ENABLED`] = features[r])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import commerceProviderConfig from '@framework/config.json'
|
import commerceProviderConfig from '../config.json'
|
||||||
import type { CommerceProviderConfig } from '../types'
|
import type { CommerceProviderConfig } from '../types'
|
||||||
import memo from 'lodash.memoize'
|
import memo from 'lodash.memoize'
|
||||||
|
|
||||||
@ -14,6 +14,16 @@ function isFeatureEnabled(config: CommerceProviderConfig) {
|
|||||||
.includes(desideredFeature)
|
.includes(desideredFeature)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toEnvConfig(
|
||||||
|
configMap: CommerceProviderConfig['features']
|
||||||
|
): Map<string, boolean> {
|
||||||
|
let toEnvConfigMap = new Map<string, boolean>()
|
||||||
|
Object.keys(configMap).map((r) =>
|
||||||
|
toEnvConfigMap.set(`${r.toUpperCase()}_ENABLED`, configMap[r])
|
||||||
|
)
|
||||||
|
return toEnvConfigMap
|
||||||
|
}
|
||||||
|
|
||||||
function boostrap(): FeaturesAPI {
|
function boostrap(): FeaturesAPI {
|
||||||
const basis = {
|
const basis = {
|
||||||
isEnabled: () => false,
|
isEnabled: () => false,
|
||||||
|
@ -1,23 +1,260 @@
|
|||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [Getting Started](#getting-started)
|
- [Getting Started](#getting-started)
|
||||||
|
- [Modifications](#modifications)
|
||||||
|
- [Adding item to Cart](#adding-item-to-cart)
|
||||||
|
- [Proceed to Checkout](#proceed-to-checkout)
|
||||||
|
- [General Usage](#general-usage)
|
||||||
|
- [CommerceProvider](#commerceprovider)
|
||||||
|
- [useCommerce](#usecommerce)
|
||||||
|
- [Hooks](#hooks)
|
||||||
|
- [usePrice](#useprice)
|
||||||
|
- [useAddItem](#useadditem)
|
||||||
|
- [useRemoveItem](#useremoveitem)
|
||||||
|
- [useUpdateItem](#useupdateitem)
|
||||||
|
- [APIs](#apis)
|
||||||
|
- [getProduct](#getproduct)
|
||||||
|
- [getAllProducts](#getallproducts)
|
||||||
|
- [getAllCollections](#getallcollections)
|
||||||
|
- [getAllPages](#getallpages)
|
||||||
|
|
||||||
# Shopify Storefront Data Hooks
|
# Shopify Storefront Data Hooks
|
||||||
|
|
||||||
Collection of hooks and data fetching functions to integrate Shopify in a React application. Designed to work with [Next.js Commerce](https://commerce-theta-ashy.vercel.app).
|
Collection of hooks and data fetching functions to integrate Shopify in a React application. Designed to work with [Next.js Commerce](https://demo.vercel.store/).
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
1. Environment variables need to be set:
|
1. Install dependencies:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
yarn install shopify-buy
|
||||||
|
yarn install -D @types/shopify-buy
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Environment variables need to be set:
|
||||||
|
|
||||||
|
```
|
||||||
|
SHOPIFY_STORE_DOMAIN=
|
||||||
|
SHOPIFY_STOREFRONT_ACCESS_TOKEN=
|
||||||
NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN=
|
NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN=
|
||||||
NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN=
|
NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN=
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Point the framework to `shopify` by updating `tsconfig.json`:
|
4. Point the framework to `shopify` by updating `tsconfig.json`:
|
||||||
|
|
||||||
```
|
```
|
||||||
"@framework/*": ["framework/shopify/*"],
|
"@framework/*": ["framework/shopify/*"],
|
||||||
"@framework": ["framework/shopify"]
|
"@framework": ["framework/shopify"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Modifications
|
||||||
|
|
||||||
|
These modifications are temporarily until contributions are made to remove them.
|
||||||
|
|
||||||
|
#### Adding item to Cart
|
||||||
|
|
||||||
|
```js
|
||||||
|
// components/product/ProductView/ProductView.tsx
|
||||||
|
const ProductView: FC<Props> = ({ product }) => {
|
||||||
|
const addToCart = async () => {
|
||||||
|
setLoading(true)
|
||||||
|
try {
|
||||||
|
await addItem({
|
||||||
|
productId: product.id,
|
||||||
|
variantId: variant ? variant.id : product.variants[0].id,
|
||||||
|
})
|
||||||
|
openSidebar()
|
||||||
|
setLoading(false)
|
||||||
|
} catch (err) {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Proceed to Checkout
|
||||||
|
|
||||||
|
```js
|
||||||
|
// components/cart/CartSidebarView/CartSidebarView.tsx
|
||||||
|
import { useCommerce } from '@framework'
|
||||||
|
|
||||||
|
const CartSidebarView: FC = () => {
|
||||||
|
const { checkout } = useCommerce()
|
||||||
|
return (
|
||||||
|
<Button href={checkout.webUrl} Component="a" width="100%">
|
||||||
|
Proceed to Checkout
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## General Usage
|
||||||
|
|
||||||
|
### CommerceProvider
|
||||||
|
|
||||||
|
Provider component that creates the commerce context for children.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { CommerceProvider } from '@framework'
|
||||||
|
|
||||||
|
const App = ({ children }) => {
|
||||||
|
return <CommerceProvider locale={locale}>{children}</CommerceProvider>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
|
```
|
||||||
|
|
||||||
|
### useCommerce
|
||||||
|
|
||||||
|
Returns the configs that are defined in the nearest `CommerceProvider`. Also provides access to Shopify's `checkout` and `shop`.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { useCommerce } from 'nextjs-commerce-shopify'
|
||||||
|
|
||||||
|
const { checkout, shop } = useCommerce()
|
||||||
|
```
|
||||||
|
|
||||||
|
- `checkout`: The information required to checkout items and pay ([Documentation](https://shopify.dev/docs/storefront-api/reference/checkouts/checkout)).
|
||||||
|
- `shop`: Represents a collection of the general settings and information about the shop ([Documentation](https://shopify.dev/docs/storefront-api/reference/online-store/shop/index)).
|
||||||
|
|
||||||
|
## Hooks
|
||||||
|
|
||||||
|
### usePrice
|
||||||
|
|
||||||
|
Display the product variant price according to currency and locale.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import usePrice from '@framework/product/use-price'
|
||||||
|
|
||||||
|
const { price } = usePrice({
|
||||||
|
amount,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Takes in either `amount` or `variant`:
|
||||||
|
|
||||||
|
- `amount`: A price value for a particular item if the amount is known.
|
||||||
|
- `variant`: A shopify product variant. Price will be extracted from the variant.
|
||||||
|
|
||||||
|
### useAddItem
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { useAddItem } from '@framework/cart'
|
||||||
|
|
||||||
|
const AddToCartButton = ({ variantId, quantity }) => {
|
||||||
|
const addItem = useAddItem()
|
||||||
|
|
||||||
|
const addToCart = async () => {
|
||||||
|
await addItem({
|
||||||
|
variantId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return <button onClick={addToCart}>Add To Cart</button>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### useRemoveItem
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { useRemoveItem } from '@framework/cart'
|
||||||
|
|
||||||
|
const RemoveButton = ({ item }) => {
|
||||||
|
const removeItem = useRemoveItem()
|
||||||
|
|
||||||
|
const handleRemove = async () => {
|
||||||
|
await removeItem({ id: item.id })
|
||||||
|
}
|
||||||
|
|
||||||
|
return <button onClick={handleRemove}>Remove</button>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### useUpdateItem
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { useUpdateItem } from '@framework/cart'
|
||||||
|
|
||||||
|
const CartItem = ({ item }) => {
|
||||||
|
const [quantity, setQuantity] = useState(item.quantity)
|
||||||
|
const updateItem = useUpdateItem(item)
|
||||||
|
|
||||||
|
const updateQuantity = async (e) => {
|
||||||
|
const val = e.target.value
|
||||||
|
await updateItem({ quantity: val })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
max={99}
|
||||||
|
min={0}
|
||||||
|
value={quantity}
|
||||||
|
onChange={updateQuantity}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## APIs
|
||||||
|
|
||||||
|
Collections of APIs to fetch data from a Shopify store.
|
||||||
|
|
||||||
|
The data is fetched using the [Shopify JavaScript Buy SDK](https://github.com/Shopify/js-buy-sdk#readme). Read the [Shopify Storefront API reference](https://shopify.dev/docs/storefront-api/reference) for more information.
|
||||||
|
|
||||||
|
### getProduct
|
||||||
|
|
||||||
|
Get a single product by its `handle`.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import getProduct from '@framework/product/get-product'
|
||||||
|
import { getConfig } from '@framework/api'
|
||||||
|
|
||||||
|
const config = getConfig()
|
||||||
|
|
||||||
|
const product = await getProduct({
|
||||||
|
variables: { slug },
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### getAllProducts
|
||||||
|
|
||||||
|
```js
|
||||||
|
import getAllProducts from '@framework/product/get-all-products'
|
||||||
|
import { getConfig } from '@framework/api'
|
||||||
|
|
||||||
|
const config = getConfig()
|
||||||
|
|
||||||
|
const { products } = await getAllProducts({
|
||||||
|
variables: { first: 12 },
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### getAllCollections
|
||||||
|
|
||||||
|
```js
|
||||||
|
import getAllCollections from '@framework/product/get-all-collections'
|
||||||
|
import { getConfig } from '@framework/api'
|
||||||
|
|
||||||
|
const config = getConfig()
|
||||||
|
|
||||||
|
const collections = await getAllCollections({
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### getAllPages
|
||||||
|
|
||||||
|
```js
|
||||||
|
import getAllPages from '@framework/common/get-all-pages'
|
||||||
|
import { getConfig } from '@framework/api'
|
||||||
|
|
||||||
|
const config = getConfig()
|
||||||
|
|
||||||
|
const pages = await getAllPages({
|
||||||
|
variables: { first: 12 },
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
21
framework/shopify/api/operations/get-all-collections.ts
Normal file
21
framework/shopify/api/operations/get-all-collections.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import Client from 'shopify-buy'
|
||||||
|
import { ShopifyConfig } from '../index'
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
config: ShopifyConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllCollections = async (options: Options) => {
|
||||||
|
const { config } = options
|
||||||
|
|
||||||
|
const client = Client.buildClient({
|
||||||
|
storefrontAccessToken: config.apiToken,
|
||||||
|
domain: config.commerceUrl,
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = await client.collection.fetchAllWithProducts()
|
||||||
|
|
||||||
|
return JSON.parse(JSON.stringify(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getAllCollections
|
27
framework/shopify/api/operations/get-page.ts
Normal file
27
framework/shopify/api/operations/get-page.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { ShopifyConfig, getConfig } from '..'
|
||||||
|
import type { Page } from '../../types'
|
||||||
|
|
||||||
|
export type { Page }
|
||||||
|
|
||||||
|
export type GetPageResult<T extends { page?: any } = { page?: Page }> = T
|
||||||
|
|
||||||
|
export type PageVariables = {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPage({
|
||||||
|
url,
|
||||||
|
variables,
|
||||||
|
config,
|
||||||
|
preview,
|
||||||
|
}: {
|
||||||
|
url?: string
|
||||||
|
variables: PageVariables
|
||||||
|
config?: ShopifyConfig
|
||||||
|
preview?: boolean
|
||||||
|
}): Promise<GetPageResult> {
|
||||||
|
config = getConfig(config)
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getPage
|
@ -1,5 +1,4 @@
|
|||||||
import { GraphQLFetcherResult } from '@commerce/api'
|
import { GraphQLFetcherResult } from '@commerce/api'
|
||||||
|
|
||||||
import { getConfig, ShopifyConfig } from '../api'
|
import { getConfig, ShopifyConfig } from '../api'
|
||||||
import { Product } from '../schema'
|
import { Product } from '../schema'
|
||||||
import getProductQuery from '../utils/queries/get-product-query'
|
import getProductQuery from '../utils/queries/get-product-query'
|
||||||
|
13
framework/shopify/utils/storage.ts
Normal file
13
framework/shopify/utils/storage.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export const getCheckoutIdFromStorage = (token: string) => {
|
||||||
|
if (window && window.sessionStorage) {
|
||||||
|
return window.sessionStorage.getItem(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setCheckoutIdInStorage = (token: string, id: string | number) => {
|
||||||
|
if (window && window.sessionStorage) {
|
||||||
|
return window.sessionStorage.setItem(token, id + '')
|
||||||
|
}
|
||||||
|
}
|
60
framework/shopify/utils/to-commerce-products.ts
Normal file
60
framework/shopify/utils/to-commerce-products.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { Product, Image } from '../types'
|
||||||
|
|
||||||
|
export default function toCommerceProducts(products: Product[]) {
|
||||||
|
return products.map((product: Product) => {
|
||||||
|
return {
|
||||||
|
id: product.id,
|
||||||
|
entityId: product.id,
|
||||||
|
name: product.title,
|
||||||
|
slug: product.handle,
|
||||||
|
title: product.title,
|
||||||
|
vendor: product.vendor,
|
||||||
|
description: product.descriptionHtml,
|
||||||
|
path: `/${product.handle}`,
|
||||||
|
price: {
|
||||||
|
value: +product.variants[0].price,
|
||||||
|
currencyCode: 'USD', // TODO
|
||||||
|
},
|
||||||
|
images: product.images.map((image: Image) => {
|
||||||
|
return {
|
||||||
|
url: image.src,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
variants: product.variants.map((variant) => {
|
||||||
|
return {
|
||||||
|
id: variant.id,
|
||||||
|
options: variant.selectedOptions.map((selectedOption) => {
|
||||||
|
return {
|
||||||
|
__typename: 'MultipleChoiceOption',
|
||||||
|
displayName: selectedOption.name,
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: variant.id,
|
||||||
|
label: selectedOption.value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
productOptions: product.options.map((option) => {
|
||||||
|
return {
|
||||||
|
__typename: 'MultipleChoiceOption',
|
||||||
|
displayName: option.name,
|
||||||
|
values: option.values.map((value) => {
|
||||||
|
return {
|
||||||
|
node: {
|
||||||
|
entityId: 1,
|
||||||
|
label: value.value,
|
||||||
|
hexColors: [value.value],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
options: [],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -1,44 +1,6 @@
|
|||||||
module.exports = {
|
const providerConfig = require('./framework/bigcommerce/config.json')
|
||||||
images: {
|
const providerNextConfig = require('./framework/bigcommerce/next.config')
|
||||||
domains: ['cdn11.bigcommerce.com', 'cdn.shopify.com'],
|
const bootstrap = require('./framework/commerce/utils/bootstrap')
|
||||||
},
|
const d = require('deepmerge')
|
||||||
i18n: {
|
|
||||||
locales: ['en-US', 'es'],
|
module.exports = d(providerNextConfig, bootstrap(providerConfig))
|
||||||
defaultLocale: 'en-US',
|
|
||||||
},
|
|
||||||
rewrites() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
source: '/checkout',
|
|
||||||
destination: '/api/bigcommerce/checkout',
|
|
||||||
},
|
|
||||||
// The logout is also an action so this route is not required, but it's also another way
|
|
||||||
// you can allow a logout!
|
|
||||||
{
|
|
||||||
source: '/logout',
|
|
||||||
destination: '/api/bigcommerce/customers/logout?redirect_to=/',
|
|
||||||
},
|
|
||||||
// Rewrites for /search
|
|
||||||
{
|
|
||||||
source: '/search/designers/:name',
|
|
||||||
destination: '/search',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: '/search/designers/:name/:category',
|
|
||||||
destination: '/search',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// This rewrite will also handle `/search/designers`
|
|
||||||
source: '/search/:category',
|
|
||||||
destination: '/search',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
typescript: {
|
|
||||||
// !! WARN !!
|
|
||||||
// Dangerously allow production builds to successfully complete even if
|
|
||||||
// your project has type errors.
|
|
||||||
// !! WARN !!
|
|
||||||
ignoreBuildErrors: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"generate": "graphql-codegen",
|
"generate": "graphql-codegen",
|
||||||
"generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js"
|
"generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js"
|
||||||
},
|
},
|
||||||
|
"sideEffects": false,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "12.x"
|
"node": "12.x"
|
||||||
@ -45,6 +46,7 @@
|
|||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"react-merge-refs": "^1.1.0",
|
"react-merge-refs": "^1.1.0",
|
||||||
"react-ticker": "^1.2.2",
|
"react-ticker": "^1.2.2",
|
||||||
|
"shopify-buy": "^2.11.0",
|
||||||
"swr": "^0.4.0",
|
"swr": "^0.4.0",
|
||||||
"tabbable": "^5.1.5",
|
"tabbable": "^5.1.5",
|
||||||
"tailwindcss": "^2.0.2"
|
"tailwindcss": "^2.0.2"
|
||||||
@ -65,6 +67,7 @@
|
|||||||
"@types/lodash.throttle": "^4.1.6",
|
"@types/lodash.throttle": "^4.1.6",
|
||||||
"@types/node": "^14.14.16",
|
"@types/node": "^14.14.16",
|
||||||
"@types/react": "^17.0.0",
|
"@types/react": "^17.0.0",
|
||||||
|
"deepmerge": "^4.2.2",
|
||||||
"graphql": "^15.4.0",
|
"graphql": "^15.4.0",
|
||||||
"husky": "^4.3.8",
|
"husky": "^4.3.8",
|
||||||
"lint-staged": "^10.5.3",
|
"lint-staged": "^10.5.3",
|
||||||
|
@ -8,7 +8,6 @@ import { getConfig } from '@framework/api'
|
|||||||
import getAllProducts from '@framework/product/get-all-products'
|
import getAllProducts from '@framework/product/get-all-products'
|
||||||
import getSiteInfo from '@framework/common/get-site-info'
|
import getSiteInfo from '@framework/common/get-site-info'
|
||||||
import getAllPages from '@framework/common/get-all-pages'
|
import getAllPages from '@framework/common/get-all-pages'
|
||||||
import Features from '@commerce/utils/features'
|
|
||||||
|
|
||||||
export async function getStaticProps({
|
export async function getStaticProps({
|
||||||
preview,
|
preview,
|
||||||
@ -24,7 +23,6 @@ export async function getStaticProps({
|
|||||||
|
|
||||||
const { categories, brands } = await getSiteInfo({ config, preview })
|
const { categories, brands } = await getSiteInfo({ config, preview })
|
||||||
const { pages } = await getAllPages({ config, preview })
|
const { pages } = await getAllPages({ config, preview })
|
||||||
const isWishlistEnabled = Features.isEnabled('wishlist')
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
@ -32,9 +30,6 @@ export async function getStaticProps({
|
|||||||
categories,
|
categories,
|
||||||
brands,
|
brands,
|
||||||
pages,
|
pages,
|
||||||
commerceFeatures: {
|
|
||||||
wishlist: isWishlistEnabled,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
revalidate: 14400,
|
revalidate: 14400,
|
||||||
}
|
}
|
||||||
@ -44,7 +39,6 @@ export default function Home({
|
|||||||
products,
|
products,
|
||||||
brands,
|
brands,
|
||||||
categories,
|
categories,
|
||||||
commerceFeatures,
|
|
||||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -57,7 +51,6 @@ export default function Home({
|
|||||||
width: i === 0 ? 1080 : 540,
|
width: i === 0 ? 1080 : 540,
|
||||||
height: i === 0 ? 1080 : 540,
|
height: i === 0 ? 1080 : 540,
|
||||||
}}
|
}}
|
||||||
wishlist={commerceFeatures.wishlist}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -71,7 +64,6 @@ export default function Home({
|
|||||||
width: 320,
|
width: 320,
|
||||||
height: 320,
|
height: 320,
|
||||||
}}
|
}}
|
||||||
wishlist={commerceFeatures.wishlist}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Marquee>
|
</Marquee>
|
||||||
@ -94,7 +86,6 @@ export default function Home({
|
|||||||
width: i === 0 ? 1080 : 540,
|
width: i === 0 ? 1080 : 540,
|
||||||
height: i === 0 ? 1080 : 540,
|
height: i === 0 ? 1080 : 540,
|
||||||
}}
|
}}
|
||||||
wishlist={commerceFeatures.wishlist}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -108,7 +99,6 @@ export default function Home({
|
|||||||
width: 320,
|
width: 320,
|
||||||
height: 320,
|
height: 320,
|
||||||
}}
|
}}
|
||||||
wishlist={commerceFeatures.wishlist}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Marquee>
|
</Marquee>
|
||||||
|
@ -11,14 +11,12 @@ import { getConfig } from '@framework/api'
|
|||||||
import getProduct from '@framework/product/get-product'
|
import getProduct from '@framework/product/get-product'
|
||||||
import getAllPages from '@framework/common/get-all-pages'
|
import getAllPages from '@framework/common/get-all-pages'
|
||||||
import getAllProductPaths from '@framework/product/get-all-product-paths'
|
import getAllProductPaths from '@framework/product/get-all-product-paths'
|
||||||
import Features from '@commerce/utils/features'
|
|
||||||
|
|
||||||
export async function getStaticProps({
|
export async function getStaticProps({
|
||||||
params,
|
params,
|
||||||
locale,
|
locale,
|
||||||
preview,
|
preview,
|
||||||
}: GetStaticPropsContext<{ slug: string }>) {
|
}: GetStaticPropsContext<{ slug: string }>) {
|
||||||
const isWishlistEnabled = Features.isEnabled('wishlist')
|
|
||||||
const config = getConfig({ locale })
|
const config = getConfig({ locale })
|
||||||
const { pages } = await getAllPages({ config, preview })
|
const { pages } = await getAllPages({ config, preview })
|
||||||
const { product } = await getProduct({
|
const { product } = await getProduct({
|
||||||
@ -35,9 +33,6 @@ export async function getStaticProps({
|
|||||||
props: {
|
props: {
|
||||||
pages,
|
pages,
|
||||||
product,
|
product,
|
||||||
commerceFeatures: {
|
|
||||||
wishlist: isWishlistEnabled,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
revalidate: 200,
|
revalidate: 200,
|
||||||
}
|
}
|
||||||
@ -62,17 +57,13 @@ export async function getStaticPaths({ locales }: GetStaticPathsContext) {
|
|||||||
|
|
||||||
export default function Slug({
|
export default function Slug({
|
||||||
product,
|
product,
|
||||||
commerceFeatures,
|
|
||||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
return router.isFallback ? (
|
return router.isFallback ? (
|
||||||
<h1>Loading...</h1> // TODO (BC) Add Skeleton Views
|
<h1>Loading...</h1> // TODO (BC) Add Skeleton Views
|
||||||
) : (
|
) : (
|
||||||
<ProductView
|
<ProductView product={product as any} />
|
||||||
product={product as any}
|
|
||||||
wishlist={commerceFeatures.wishlist}
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,14 +26,13 @@ const SORT = Object.entries({
|
|||||||
'price-desc': 'Price: High to low',
|
'price-desc': 'Price: High to low',
|
||||||
})
|
})
|
||||||
|
|
||||||
import Features from '@commerce/utils/features'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
filterQuery,
|
filterQuery,
|
||||||
getCategoryPath,
|
getCategoryPath,
|
||||||
getDesignerPath,
|
getDesignerPath,
|
||||||
useSearchMeta,
|
useSearchMeta,
|
||||||
} from '@lib/search'
|
} from '@lib/search'
|
||||||
|
import { Product } from '@commerce/types'
|
||||||
|
|
||||||
export async function getStaticProps({
|
export async function getStaticProps({
|
||||||
preview,
|
preview,
|
||||||
@ -42,15 +41,11 @@ export async function getStaticProps({
|
|||||||
const config = getConfig({ locale })
|
const config = getConfig({ locale })
|
||||||
const { pages } = await getAllPages({ config, preview })
|
const { pages } = await getAllPages({ config, preview })
|
||||||
const { categories, brands } = await getSiteInfo({ config, preview })
|
const { categories, brands } = await getSiteInfo({ config, preview })
|
||||||
const isWishlistEnabled = Features.isEnabled('wishlist')
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
pages,
|
pages,
|
||||||
categories,
|
categories,
|
||||||
brands,
|
brands,
|
||||||
commerceFeatures: {
|
|
||||||
wishlist: isWishlistEnabled,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,7 +53,6 @@ export async function getStaticProps({
|
|||||||
export default function Search({
|
export default function Search({
|
||||||
categories,
|
categories,
|
||||||
brands,
|
brands,
|
||||||
commerceFeatures: { wishlist },
|
|
||||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||||
const [activeFilter, setActiveFilter] = useState('')
|
const [activeFilter, setActiveFilter] = useState('')
|
||||||
const [toggleFilter, setToggleFilter] = useState(false)
|
const [toggleFilter, setToggleFilter] = useState(false)
|
||||||
@ -358,7 +352,6 @@ export default function Search({
|
|||||||
width: 480,
|
width: 480,
|
||||||
height: 480,
|
height: 480,
|
||||||
}}
|
}}
|
||||||
wishlist={wishlist}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -11,14 +11,13 @@ import { useCustomer } from '@framework/customer'
|
|||||||
import { WishlistCard } from '@components/wishlist'
|
import { WishlistCard } from '@components/wishlist'
|
||||||
import useWishlist from '@framework/wishlist/use-wishlist'
|
import useWishlist from '@framework/wishlist/use-wishlist'
|
||||||
import getAllPages from '@framework/common/get-all-pages'
|
import getAllPages from '@framework/common/get-all-pages'
|
||||||
import Features from '@commerce/utils/features'
|
|
||||||
|
|
||||||
export async function getStaticProps({
|
export async function getStaticProps({
|
||||||
preview,
|
preview,
|
||||||
locale,
|
locale,
|
||||||
}: GetStaticPropsContext) {
|
}: GetStaticPropsContext) {
|
||||||
// Disabling page if Feature is not available
|
// Disabling page if Feature is not available
|
||||||
if (Features.isEnabled('wishlist')) {
|
if (!process.env.COMMERCE_WISHLIST_ENABLED) {
|
||||||
return {
|
return {
|
||||||
notFound: true,
|
notFound: true,
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,10 @@
|
|||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@lib/*": ["lib/*"],
|
"@lib/*": ["lib/*"],
|
||||||
"@assets/*": ["assets/*"],
|
|
||||||
"@config/*": ["config/*"],
|
|
||||||
"@components/*": ["components/*"],
|
|
||||||
"@utils/*": ["utils/*"],
|
"@utils/*": ["utils/*"],
|
||||||
"@commerce/*": ["framework/commerce/*"],
|
"@config/*": ["config/*"],
|
||||||
|
"@assets/*": ["assets/*"],
|
||||||
|
"@components/*": ["components/*"],
|
||||||
"@commerce": ["framework/commerce"],
|
"@commerce": ["framework/commerce"],
|
||||||
"@framework/*": ["framework/shopify/*"],
|
"@framework/*": ["framework/shopify/*"],
|
||||||
"@framework": ["framework/shopify"]
|
"@framework": ["framework/shopify"]
|
||||||
|
10
yarn.lock
10
yarn.lock
@ -2532,6 +2532,11 @@ deep-is@~0.1.3:
|
|||||||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
|
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
|
||||||
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
|
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
|
||||||
|
|
||||||
|
deepmerge@^4.2.2:
|
||||||
|
version "4.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
|
||||||
|
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
|
||||||
|
|
||||||
defaults@^1.0.3:
|
defaults@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
|
resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
|
||||||
@ -6221,6 +6226,11 @@ shell-quote@1.7.2:
|
|||||||
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2"
|
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2"
|
||||||
integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==
|
integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==
|
||||||
|
|
||||||
|
shopify-buy@^2.11.0:
|
||||||
|
version "2.11.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/shopify-buy/-/shopify-buy-2.11.0.tgz#0f7cb52741395e4ae778c336f32ddf3fe67c2f35"
|
||||||
|
integrity sha512-bGjS1b/VCPvCjazSstlKwgLtK1WBotWom06/12loja8yfo/cWkLuJsakBbQe1uEIDiOLhKaR0M0CAXZFheYDug==
|
||||||
|
|
||||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user