Merge branch 'agnostic' of https://github.com/vercel/commerce into agnostic

This commit is contained in:
cond0r 2021-02-25 00:58:22 +02:00
commit ce2a410cb3
43 changed files with 500 additions and 158 deletions

View File

@ -2,4 +2,6 @@ BIGCOMMERCE_STOREFRONT_API_URL=
BIGCOMMERCE_STOREFRONT_API_TOKEN=
BIGCOMMERCE_STORE_API_URL=
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
View File

@ -0,0 +1,3 @@
{
"recommendations": ["esbenp.prettier-vscode"]
}

View File

@ -1,4 +0,0 @@
## Changelog
- Select Variants Working
- Click on cart item title, closes the sidebar

View File

@ -57,18 +57,21 @@ Main folder and its exposed functions
- getAllProducts
- `wishlist`
- useWishlist
- addWishlistItem
- removeWishlistItem
- useAddItem
- useRemoveItem
- `auth`
- useLogin
- useLogout
- useSignup
- `customer`
- useCustomer
- getCustomerId
- getCustomerWistlist
- `cart`
- useCart
- useAddItem
- useRemoveItem
- useCartActions
- useUpdateItem
- `config.json`

View File

@ -9,7 +9,7 @@ import usePrice from '@framework/product/use-price'
import CartItem from '../CartItem'
import s from './CartSidebarView.module.css'
const CartSidebarView: FC<{ wishlist?: boolean }> = ({ wishlist }) => {
const CartSidebarView: FC = () => {
const { closeSidebar } = useUI()
const { data, isLoading, isEmpty } = useCart()
@ -48,7 +48,7 @@ const CartSidebarView: FC<{ wishlist?: boolean }> = ({ wishlist }) => {
</button>
</div>
<div className="space-y-1">
<UserNav wishlist={wishlist} />
<UserNav />
</div>
</div>
</header>

View File

@ -5,20 +5,17 @@ import { Grid } from '@components/ui'
import { ProductCard } from '@components/product'
import s from './HomeAllProductsGrid.module.css'
import { getCategoryPath, getDesignerPath } from '@lib/search'
import wishlist from '@framework/api/wishlist'
interface Props {
categories?: any
brands?: any
products?: Product[]
wishlist?: boolean
}
const HomeAllProductsGrid: FC<Props> = ({
categories,
brands,
products = [],
wishlist = false,
}) => {
return (
<div className={s.root}>
@ -65,7 +62,6 @@ const HomeAllProductsGrid: FC<Props> = ({
width: 480,
height: 480,
}}
wishlist={wishlist}
/>
))}
</Grid>

View File

@ -58,11 +58,10 @@ const Layout: FC<Props> = ({
} = useUI()
const { acceptedCookies, onAcceptCookies } = useAcceptCookies()
const { locale = 'en-US' } = useRouter()
const isWishlistEnabled = commerceFeatures?.wishlist
return (
<CommerceProvider locale={locale}>
<div className={cn(s.root)}>
<Navbar wishlist={isWishlistEnabled} />
<Navbar />
<main className="fit">{children}</main>
<Footer pages={pageProps.pages} />
@ -73,7 +72,7 @@ const Layout: FC<Props> = ({
</Modal>
<Sidebar open={displaySidebar} onClose={closeSidebar}>
<CartSidebarView wishlist={isWishlistEnabled} />
<CartSidebarView />
</Sidebar>
<FeatureBar

View File

@ -5,7 +5,7 @@ import { Searchbar, UserNav } from '@components/common'
import NavbarRoot from './NavbarRoot'
import s from './Navbar.module.css'
const Navbar: FC<{ wishlist?: boolean }> = ({ wishlist }) => (
const Navbar: FC = () => (
<NavbarRoot>
<Container>
<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 className="flex justify-end flex-1 space-x-8">
<UserNav wishlist={wishlist} />
<UserNav />
</div>
</div>

View File

@ -4,20 +4,19 @@ import cn from 'classnames'
import type { LineItem } from '@framework/types'
import useCart from '@framework/cart/use-cart'
import useCustomer from '@framework/customer/use-customer'
import { Avatar } from '@components/common'
import { Heart, Bag } from '@components/icons'
import { useUI } from '@components/ui/context'
import DropdownMenu from './DropdownMenu'
import s from './UserNav.module.css'
import { Avatar } from '@components/common'
interface Props {
className?: string
wishlist?: boolean
}
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: customer } = useCustomer()
const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI()
@ -31,7 +30,7 @@ const UserNav: FC<Props> = ({ className, wishlist = false }) => {
<Bag />
{itemsCount > 0 && <span className={s.bagCount}>{itemsCount}</span>}
</li>
{wishlist && (
{process.env.COMMERCE_WISHLIST_ENABLED && (
<li className={s.item}>
<Link href="/wishlist">
<a onClick={closeSidebarIfPresent} aria-label="Wishlist">

View File

@ -11,7 +11,6 @@ interface Props {
product: Product
variant?: 'slim' | 'simple'
imgProps?: Omit<ImageProps, 'src'>
wishlist?: boolean
}
const placeholderImg = '/product-img-placeholder.svg'
@ -21,7 +20,6 @@ const ProductCard: FC<Props> = ({
product,
variant,
imgProps,
wishlist = false,
...props
}) => (
<Link href={`/product/${product.slug}`} {...props}>
@ -59,11 +57,11 @@ const ProductCard: FC<Props> = ({
{product.price.currencyCode}
</span>
</div>
{wishlist && (
{process.env.COMMERCE_WISHLIST_ENABLED && (
<WishlistButton
className={s.wishlistButton}
productId={product.id}
variant={product.variants[0]}
variant={product.variants[0] as any}
/>
)}
</div>

View File

@ -4,9 +4,8 @@ import { NextSeo } from 'next-seo'
import { FC, useState } from 'react'
import s from './ProductView.module.css'
import { useUI } from '@components/ui'
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 usePrice from '@framework/product/use-price'
@ -19,10 +18,9 @@ interface Props {
className?: string
children?: any
product: Product
wishlist?: boolean
}
const ProductView: FC<Props> = ({ product, wishlist = false }) => {
const ProductView: FC<Props> = ({ product }) => {
const addItem = useAddItem()
const { price } = usePrice({
amount: product.price.value,
@ -101,7 +99,6 @@ const ProductView: FC<Props> = ({ product, wishlist = false }) => {
</ProductSlider>
</div>
</div>
<div className={s.sidebar}>
<section>
{product.options?.map((opt) => (
@ -152,11 +149,11 @@ const ProductView: FC<Props> = ({ product, wishlist = false }) => {
</Button>
</div>
</div>
{wishlist && (
{process.env.COMMERCE_WISHLIST_ENABLED && (
<WishlistButton
className={s.wishlistButton}
productId={product.id}
variant={product.variants[0]!}
variant={product.variants[0]! as any}
/>
)}
</div>

View File

@ -8,6 +8,7 @@ export interface State {
displayToast: boolean
modalView: string
toastText: string
userAvatar: string
}
const initialState = {
@ -17,6 +18,7 @@ const initialState = {
modalView: 'LOGIN_VIEW',
displayToast: false,
toastText: '',
userAvatar: '',
}
type Action =

View File

@ -1,13 +1,12 @@
import React, { FC, useState } from 'react'
import cn from 'classnames'
import { Heart } from '@components/icons'
import { useUI } from '@components/ui'
import type { Product, ProductVariant } from '@commerce/types'
import useCustomer from '@framework/customer/use-customer'
import useAddItem from '@framework/wishlist/use-add-item'
import useCustomer from '@framework/customer/use-customer'
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 = {
productId: Product['id']
@ -28,7 +27,8 @@ const WishlistButton: FC<Props> = ({
const [loading, setLoading] = useState(false)
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) => {

View File

@ -1,7 +1,7 @@
import { useCallback } from 'react'
import type { MutationHook } from '@commerce/utils/types'
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 useCustomer from '../customer/use-customer'

View File

@ -1,6 +1,6 @@
import { useCallback } from 'react'
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'
export default useLogout as UseLogout<typeof handler>

View File

@ -1,7 +1,7 @@
import { useCallback } from 'react'
import type { MutationHook } from '@commerce/utils/types'
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 useCustomer from '../customer/use-customer'

View File

@ -1,5 +1,5 @@
{
"features": {
"wishlist": false
"wishlist": true
}
}

View 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',
},
]
},
}

View File

@ -1,2 +1,2 @@
export * from '@commerce/use-price'
export { default } from '@commerce/use-price'
export * from '@commerce/product/use-price'
export { default } from '@commerce/product/use-price'

View File

@ -1,4 +1,3 @@
export { default as useAddItem } from './use-add-item'
export { default as useWishlist } from './use-wishlist'
export { default as useRemoveItem } from './use-remove-item'
export { default as useWishlistActions } from './use-wishlist-actions'

View File

@ -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 }
}

View File

@ -1,7 +1,7 @@
import { useHook, useMutationHook } from './utils/use-hook'
import { mutationFetcher } from './utils/default-fetcher'
import type { MutationHook, HookFetcherFn } from './utils/types'
import type { Provider } from '.'
import { useHook, useMutationHook } from '../utils/use-hook'
import { mutationFetcher } from '../utils/default-fetcher'
import type { MutationHook, HookFetcherFn } from '../utils/types'
import type { Provider } from '..'
export type UseLogin<
H extends MutationHook<any, any, any> = MutationHook<null, {}, {}>

View File

@ -1,7 +1,7 @@
import { useHook, useMutationHook } from './utils/use-hook'
import { mutationFetcher } from './utils/default-fetcher'
import type { HookFetcherFn, MutationHook } from './utils/types'
import type { Provider } from '.'
import { useHook, useMutationHook } from '../utils/use-hook'
import { mutationFetcher } from '../utils/default-fetcher'
import type { HookFetcherFn, MutationHook } from '../utils/types'
import type { Provider } from '..'
export type UseLogout<
H extends MutationHook<any, any, any> = MutationHook<null>

View File

@ -1,7 +1,7 @@
import { useHook, useMutationHook } from './utils/use-hook'
import { mutationFetcher } from './utils/default-fetcher'
import type { HookFetcherFn, MutationHook } from './utils/types'
import type { Provider } from '.'
import { useHook, useMutationHook } from '../utils/use-hook'
import { mutationFetcher } from '../utils/default-fetcher'
import type { HookFetcherFn, MutationHook } from '../utils/types'
import type { Provider } from '..'
export type UseSignup<
H extends MutationHook<any, any, any> = MutationHook<null>

View File

@ -1,5 +0,0 @@
{
"features": {
"wishlist": true
}
}

View File

@ -1,5 +1,5 @@
import { useMemo } from 'react'
import { useCommerce } from '.'
import { useCommerce } from '..'
export function formatPrice({
amount,

View File

@ -2,8 +2,10 @@ import type { Wishlist as BCWishlist } from '@framework/api/wishlist'
import type { Customer as BCCustomer } from '@framework/api/customers'
import type { SearchProductsData as BCSearchProductsData } from '@framework/api/catalog/products'
export type Features = 'wishlist' | 'checkout' | string
export type CommerceProviderConfig = {
features: Record<string, boolean>
features: Record<Features, boolean>
}
export type Discount = {

11
framework/commerce/utils/bootstrap.js vendored Normal file
View 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
}

View File

@ -1,4 +1,4 @@
import commerceProviderConfig from '@framework/config.json'
import commerceProviderConfig from '../config.json'
import type { CommerceProviderConfig } from '../types'
import memo from 'lodash.memoize'
@ -14,6 +14,16 @@ function isFeatureEnabled(config: CommerceProviderConfig) {
.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 {
const basis = {
isEnabled: () => false,

View File

@ -1,23 +1,260 @@
## Table of Contents
- [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
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
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_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"]
```
### 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,
})
```

View 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

View 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

View File

@ -1,5 +1,4 @@
import { GraphQLFetcherResult } from '@commerce/api'
import { getConfig, ShopifyConfig } from '../api'
import { Product } from '../schema'
import getProductQuery from '../utils/queries/get-product-query'

View 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 + '')
}
}

View 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: [],
}
})
}

View File

@ -1,44 +1,6 @@
module.exports = {
images: {
domains: ['cdn11.bigcommerce.com', 'cdn.shopify.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',
},
]
},
typescript: {
// !! WARN !!
// Dangerously allow production builds to successfully complete even if
// your project has type errors.
// !! WARN !!
ignoreBuildErrors: true,
},
}
const providerConfig = require('./framework/bigcommerce/config.json')
const providerNextConfig = require('./framework/bigcommerce/next.config')
const bootstrap = require('./framework/commerce/utils/bootstrap')
const d = require('deepmerge')
module.exports = d(providerNextConfig, bootstrap(providerConfig))

View File

@ -11,6 +11,7 @@
"generate": "graphql-codegen",
"generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js"
},
"sideEffects": false,
"license": "MIT",
"engines": {
"node": "12.x"
@ -45,6 +46,7 @@
"react-dom": "^17.0.1",
"react-merge-refs": "^1.1.0",
"react-ticker": "^1.2.2",
"shopify-buy": "^2.11.0",
"swr": "^0.4.0",
"tabbable": "^5.1.5",
"tailwindcss": "^2.0.2"
@ -65,6 +67,7 @@
"@types/lodash.throttle": "^4.1.6",
"@types/node": "^14.14.16",
"@types/react": "^17.0.0",
"deepmerge": "^4.2.2",
"graphql": "^15.4.0",
"husky": "^4.3.8",
"lint-staged": "^10.5.3",

View File

@ -8,7 +8,6 @@ import { getConfig } from '@framework/api'
import getAllProducts from '@framework/product/get-all-products'
import getSiteInfo from '@framework/common/get-site-info'
import getAllPages from '@framework/common/get-all-pages'
import Features from '@commerce/utils/features'
export async function getStaticProps({
preview,
@ -24,7 +23,6 @@ export async function getStaticProps({
const { categories, brands } = await getSiteInfo({ config, preview })
const { pages } = await getAllPages({ config, preview })
const isWishlistEnabled = Features.isEnabled('wishlist')
return {
props: {
@ -32,9 +30,6 @@ export async function getStaticProps({
categories,
brands,
pages,
commerceFeatures: {
wishlist: isWishlistEnabled,
},
},
revalidate: 14400,
}
@ -44,7 +39,6 @@ export default function Home({
products,
brands,
categories,
commerceFeatures,
}: InferGetStaticPropsType<typeof getStaticProps>) {
return (
<>
@ -57,7 +51,6 @@ export default function Home({
width: i === 0 ? 1080 : 540,
height: i === 0 ? 1080 : 540,
}}
wishlist={commerceFeatures.wishlist}
/>
))}
</Grid>
@ -71,7 +64,6 @@ export default function Home({
width: 320,
height: 320,
}}
wishlist={commerceFeatures.wishlist}
/>
))}
</Marquee>
@ -94,7 +86,6 @@ export default function Home({
width: i === 0 ? 1080 : 540,
height: i === 0 ? 1080 : 540,
}}
wishlist={commerceFeatures.wishlist}
/>
))}
</Grid>
@ -108,7 +99,6 @@ export default function Home({
width: 320,
height: 320,
}}
wishlist={commerceFeatures.wishlist}
/>
))}
</Marquee>

View File

@ -11,14 +11,12 @@ import { getConfig } from '@framework/api'
import getProduct from '@framework/product/get-product'
import getAllPages from '@framework/common/get-all-pages'
import getAllProductPaths from '@framework/product/get-all-product-paths'
import Features from '@commerce/utils/features'
export async function getStaticProps({
params,
locale,
preview,
}: GetStaticPropsContext<{ slug: string }>) {
const isWishlistEnabled = Features.isEnabled('wishlist')
const config = getConfig({ locale })
const { pages } = await getAllPages({ config, preview })
const { product } = await getProduct({
@ -35,9 +33,6 @@ export async function getStaticProps({
props: {
pages,
product,
commerceFeatures: {
wishlist: isWishlistEnabled,
},
},
revalidate: 200,
}
@ -62,17 +57,13 @@ export async function getStaticPaths({ locales }: GetStaticPathsContext) {
export default function Slug({
product,
commerceFeatures,
}: InferGetStaticPropsType<typeof getStaticProps>) {
const router = useRouter()
return router.isFallback ? (
<h1>Loading...</h1> // TODO (BC) Add Skeleton Views
) : (
<ProductView
product={product as any}
wishlist={commerceFeatures.wishlist}
/>
<ProductView product={product as any} />
)
}

View File

@ -26,14 +26,13 @@ const SORT = Object.entries({
'price-desc': 'Price: High to low',
})
import Features from '@commerce/utils/features'
import {
filterQuery,
getCategoryPath,
getDesignerPath,
useSearchMeta,
} from '@lib/search'
import { Product } from '@commerce/types'
export async function getStaticProps({
preview,
@ -42,15 +41,11 @@ export async function getStaticProps({
const config = getConfig({ locale })
const { pages } = await getAllPages({ config, preview })
const { categories, brands } = await getSiteInfo({ config, preview })
const isWishlistEnabled = Features.isEnabled('wishlist')
return {
props: {
pages,
categories,
brands,
commerceFeatures: {
wishlist: isWishlistEnabled,
},
},
}
}
@ -58,7 +53,6 @@ export async function getStaticProps({
export default function Search({
categories,
brands,
commerceFeatures: { wishlist },
}: InferGetStaticPropsType<typeof getStaticProps>) {
const [activeFilter, setActiveFilter] = useState('')
const [toggleFilter, setToggleFilter] = useState(false)
@ -358,7 +352,6 @@ export default function Search({
width: 480,
height: 480,
}}
wishlist={wishlist}
/>
))}
</Grid>

View File

@ -11,14 +11,13 @@ import { useCustomer } from '@framework/customer'
import { WishlistCard } from '@components/wishlist'
import useWishlist from '@framework/wishlist/use-wishlist'
import getAllPages from '@framework/common/get-all-pages'
import Features from '@commerce/utils/features'
export async function getStaticProps({
preview,
locale,
}: GetStaticPropsContext) {
// Disabling page if Feature is not available
if (Features.isEnabled('wishlist')) {
if (!process.env.COMMERCE_WISHLIST_ENABLED) {
return {
notFound: true,
}

View File

@ -16,11 +16,10 @@
"jsx": "preserve",
"paths": {
"@lib/*": ["lib/*"],
"@assets/*": ["assets/*"],
"@config/*": ["config/*"],
"@components/*": ["components/*"],
"@utils/*": ["utils/*"],
"@commerce/*": ["framework/commerce/*"],
"@config/*": ["config/*"],
"@assets/*": ["assets/*"],
"@components/*": ["components/*"],
"@commerce": ["framework/commerce"],
"@framework/*": ["framework/shopify/*"],
"@framework": ["framework/shopify"]

View File

@ -2532,6 +2532,11 @@ deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
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:
version "1.0.3"
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"
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:
version "3.0.3"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"