From b8a0bb5a21029cc037a8af228d1406226ee311b6 Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 29 Jan 2021 11:22:41 -0300 Subject: [PATCH 01/11] Progress --- CHANGELOG.md | 5 +++-- components/product/ProductView/ProductView.tsx | 5 ++--- .../wishlist/WishlistButton/WishlistButton.tsx | 6 +++--- tsconfig.json | 12 ++++++------ 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d1d95638..b21b673f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ## Changelog -- Select Variants Working -- Click on cart item title, closes the sidebar +- Select Variants Fully Working +- Click on the Cart Item Title, closes the sidebar. + diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index d3d11ea16..448dfb10a 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -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 usePrice from '@framework/product/use-price' import { useAddItem } from '@framework/cart' @@ -100,8 +99,8 @@ const ProductView: FC = ({ product }) => { ))} - +
{product.options?.map((opt) => ( diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index dced18a89..19ae3ec1a 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -1,12 +1,12 @@ import React, { FC, useState } from 'react' import cn from 'classnames' -import { Heart } from '@components/icons' import { useUI } from '@components/ui' -import useCustomer from '@framework/customer/use-customer' +import { Heart } from '@components/icons' import useAddItem from '@framework/wishlist/use-add-item' +import useWishlist from '@framework/wishlist/use-wishlist' +import useCustomer from '@framework/customer/use-customer' import useRemoveItem from '@framework/wishlist/use-remove-item' -import useWishlist from '@framework/wishlist/use-add-item' type Props = { productId: Product['id'] diff --git a/tsconfig.json b/tsconfig.json index 67de1ee36..480622bb4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,16 +16,16 @@ "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/bigcommerce/*"], + "@commerce/*": ["framework/commerce/*"], "@framework": ["framework/bigcommerce"] + "@framework/*": ["framework/bigcommerce/*"], } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], - "exclude": ["node_modules", "components/wishlist"] + "exclude": ["node_modules"] } From 3f30344619730ce20eac92c0c8b0d66b3d18955a Mon Sep 17 00:00:00 2001 From: okbel Date: Thu, 4 Feb 2021 12:29:20 -0300 Subject: [PATCH 02/11] userAvatar --- components/common/Avatar/Avatar.tsx | 14 +++++--------- components/common/UserNav/UserNav.tsx | 2 +- components/ui/context.tsx | 16 ++++++++++++++++ lib/hooks/useUserAvatar.ts | 26 ++++++++++++++++++++++++++ tsconfig.json | 4 ++-- 5 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 lib/hooks/useUserAvatar.ts diff --git a/components/common/Avatar/Avatar.tsx b/components/common/Avatar/Avatar.tsx index 351a117ec..ab91d7089 100644 --- a/components/common/Avatar/Avatar.tsx +++ b/components/common/Avatar/Avatar.tsx @@ -1,5 +1,5 @@ -import { FC, useState, useMemo, useRef, useEffect } from 'react' -import { getRandomPairOfColors } from '@lib/colors' +import { FC, useRef, useEffect } from 'react' +import { useUserAvatar } from '@lib/hooks/useUserAvatar' interface Props { className?: string @@ -7,18 +7,14 @@ interface Props { } const Avatar: FC = ({}) => { - const [bg] = useState(useMemo(() => getRandomPairOfColors, [])) let ref = useRef() as React.MutableRefObject - - useEffect(() => { - if (ref && ref.current) { - ref.current.style.backgroundImage = `linear-gradient(140deg, ${bg[0]}, ${bg[1]} 100%)` - } - }, [bg]) + let { userAvatar } = useUserAvatar() + console.log(userAvatar) return (
{/* Add an image - We're generating a gradient as placeholder */} diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index f8e6373d9..e33796927 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -3,11 +3,11 @@ import Link from 'next/link' import cn from 'classnames' 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 diff --git a/components/ui/context.tsx b/components/ui/context.tsx index 206573858..13992a736 100644 --- a/components/ui/context.tsx +++ b/components/ui/context.tsx @@ -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 = @@ -52,6 +54,10 @@ type Action = type: 'SET_MODAL_VIEW' view: MODAL_VIEWS } + | { + type: 'SET_USER_AVATAR' + value: string + } type MODAL_VIEWS = 'SIGNUP_VIEW' | 'LOGIN_VIEW' | 'FORGOT_VIEW' type ToastText = string @@ -123,6 +129,12 @@ function uiReducer(state: State, action: Action) { toastText: action.text, } } + case 'SET_USER_AVATAR': { + return { + ...state, + userAvatar: action.value, + } + } } } @@ -147,6 +159,9 @@ export const UIProvider: FC = (props) => { const openToast = () => dispatch({ type: 'OPEN_TOAST' }) const closeToast = () => dispatch({ type: 'CLOSE_TOAST' }) + const setUserAvatar = (value: string) => + dispatch({ type: 'SET_USER_AVATAR', value }) + const setModalView = (view: MODAL_VIEWS) => dispatch({ type: 'SET_MODAL_VIEW', view }) @@ -164,6 +179,7 @@ export const UIProvider: FC = (props) => { setModalView, openToast, closeToast, + setUserAvatar, }), [state] ) diff --git a/lib/hooks/useUserAvatar.ts b/lib/hooks/useUserAvatar.ts new file mode 100644 index 000000000..bc9020931 --- /dev/null +++ b/lib/hooks/useUserAvatar.ts @@ -0,0 +1,26 @@ +import { useEffect } from 'react' +import { getRandomPairOfColors } from '@lib/colors' +import { useUI } from '@components/ui/context' + +export const useUserAvatar = (name = 'userAvatar') => { + const { userAvatar, setUserAvatar } = useUI() + + useEffect(() => { + if (!userAvatar && localStorage.getItem(name)) { + // get bg value locally. + setUserAvatar(localStorage.getItem(name)) + } + if (!localStorage.getItem(name)) { + // local not set, set. + const bg = getRandomPairOfColors() + const value = `linear-gradient(140deg, ${bg[0]}, ${bg[1]} 100%)` + localStorage.setItem(name, value) + setUserAvatar(value) + } + }, []) + + return { + userAvatar, + setUserAvatar, + } +} diff --git a/tsconfig.json b/tsconfig.json index 480622bb4..f8161ccf2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,8 +22,8 @@ "@components/*": ["components/*"], "@commerce": ["framework/commerce"], "@commerce/*": ["framework/commerce/*"], - "@framework": ["framework/bigcommerce"] - "@framework/*": ["framework/bigcommerce/*"], + "@framework": ["framework/bigcommerce"], + "@framework/*": ["framework/bigcommerce/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], From 300d04c1acadcc4e162c7d9810b6e2730f64c2c4 Mon Sep 17 00:00:00 2001 From: Peter Mekhaeil <4616064+petermekhaeil@users.noreply.github.com> Date: Fri, 12 Feb 2021 22:14:16 +0800 Subject: [PATCH 03/11] Shopify Provider (#186) * Start of Shopify provider * add missing comment to documentation * add missing env vars to documentation * update reference to types file --- .env.template | 4 +- framework/shopify/README.md | 260 ++++++++++++++++++ framework/shopify/api/cart/index.ts | 1 + framework/shopify/api/catalog/index.ts | 1 + framework/shopify/api/catalog/products.ts | 1 + framework/shopify/api/checkout/index.ts | 1 + framework/shopify/api/customers/index.ts | 1 + framework/shopify/api/customers/login.ts | 1 + framework/shopify/api/customers/logout.ts | 1 + framework/shopify/api/customers/signup.ts | 1 + framework/shopify/api/index.ts | 60 ++++ .../api/operations/get-all-collections.ts | 21 ++ framework/shopify/api/operations/get-page.ts | 27 ++ .../shopify/api/utils/fetch-graphql-api.ts | 51 ++++ framework/shopify/api/wishlist/index.tsx | 2 + framework/shopify/auth/use-login.tsx | 13 + framework/shopify/auth/use-logout.tsx | 13 + framework/shopify/auth/use-signup.tsx | 13 + framework/shopify/cart/index.ts | 5 + framework/shopify/cart/use-add-item.tsx | 30 ++ framework/shopify/cart/use-cart.tsx | 42 +++ framework/shopify/cart/use-remove-item.tsx | 17 ++ framework/shopify/cart/use-update-item.tsx | 24 ++ framework/shopify/common/get-all-pages.ts | 55 ++++ framework/shopify/common/get-site-info.ts | 30 ++ framework/shopify/customer/use-customer.tsx | 32 +++ framework/shopify/index.tsx | 109 ++++++++ .../shopify/product/get-all-product-paths.ts | 31 +++ framework/shopify/product/get-all-products.ts | 40 +++ framework/shopify/product/get-product.ts | 37 +++ framework/shopify/product/use-price.tsx | 2 + framework/shopify/product/use-search.tsx | 41 +++ framework/shopify/types.ts | 130 +++++++++ framework/shopify/utils/storage.ts | 13 + .../shopify/utils/to-commerce-products.ts | 60 ++++ framework/shopify/wishlist/use-add-item.tsx | 13 + .../shopify/wishlist/use-remove-item.tsx | 17 ++ framework/shopify/wishlist/use-wishlist.tsx | 45 +++ next.config.js | 2 +- 39 files changed, 1245 insertions(+), 2 deletions(-) create mode 100644 framework/shopify/README.md create mode 100644 framework/shopify/api/cart/index.ts create mode 100644 framework/shopify/api/catalog/index.ts create mode 100644 framework/shopify/api/catalog/products.ts create mode 100644 framework/shopify/api/checkout/index.ts create mode 100644 framework/shopify/api/customers/index.ts create mode 100644 framework/shopify/api/customers/login.ts create mode 100644 framework/shopify/api/customers/logout.ts create mode 100644 framework/shopify/api/customers/signup.ts create mode 100644 framework/shopify/api/index.ts create mode 100644 framework/shopify/api/operations/get-all-collections.ts create mode 100644 framework/shopify/api/operations/get-page.ts create mode 100644 framework/shopify/api/utils/fetch-graphql-api.ts create mode 100644 framework/shopify/api/wishlist/index.tsx create mode 100644 framework/shopify/auth/use-login.tsx create mode 100644 framework/shopify/auth/use-logout.tsx create mode 100644 framework/shopify/auth/use-signup.tsx create mode 100644 framework/shopify/cart/index.ts create mode 100644 framework/shopify/cart/use-add-item.tsx create mode 100644 framework/shopify/cart/use-cart.tsx create mode 100644 framework/shopify/cart/use-remove-item.tsx create mode 100644 framework/shopify/cart/use-update-item.tsx create mode 100644 framework/shopify/common/get-all-pages.ts create mode 100644 framework/shopify/common/get-site-info.ts create mode 100644 framework/shopify/customer/use-customer.tsx create mode 100644 framework/shopify/index.tsx create mode 100644 framework/shopify/product/get-all-product-paths.ts create mode 100644 framework/shopify/product/get-all-products.ts create mode 100644 framework/shopify/product/get-product.ts create mode 100644 framework/shopify/product/use-price.tsx create mode 100644 framework/shopify/product/use-search.tsx create mode 100644 framework/shopify/types.ts create mode 100644 framework/shopify/utils/storage.ts create mode 100644 framework/shopify/utils/to-commerce-products.ts create mode 100644 framework/shopify/wishlist/use-add-item.tsx create mode 100644 framework/shopify/wishlist/use-remove-item.tsx create mode 100644 framework/shopify/wishlist/use-wishlist.tsx diff --git a/.env.template b/.env.template index 73a8a6e3b..7d8400baf 100644 --- a/.env.template +++ b/.env.template @@ -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= \ No newline at end of file +BIGCOMMERCE_STORE_API_CLIENT_ID= +SHOPIFY_STORE_DOMAIN= +SHOPIFY_STOREFRONT_ACCESS_TOKEN= \ No newline at end of file diff --git a/framework/shopify/README.md b/framework/shopify/README.md new file mode 100644 index 000000000..fc6a70ce3 --- /dev/null +++ b/framework/shopify/README.md @@ -0,0 +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://demo.vercel.store/). + +## Getting Started + +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= +``` + +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 = ({ 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 ( + + ) +} +``` + +## General Usage + +### CommerceProvider + +Provider component that creates the commerce context for children. + +```js +import { CommerceProvider } from '@framework' + +const App = ({ children }) => { + return {children} +} + +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 +} +``` + +### useRemoveItem + +```js +import { useRemoveItem } from '@framework/cart' + +const RemoveButton = ({ item }) => { + const removeItem = useRemoveItem() + + const handleRemove = async () => { + await removeItem({ id: item.id }) + } + + return +} +``` + +### 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 ( + + ) +} +``` + +## 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, +}) +``` diff --git a/framework/shopify/api/cart/index.ts b/framework/shopify/api/cart/index.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/cart/index.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/catalog/index.ts b/framework/shopify/api/catalog/index.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/catalog/index.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/catalog/products.ts b/framework/shopify/api/catalog/products.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/catalog/products.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/checkout/index.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/customers/index.ts b/framework/shopify/api/customers/index.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/customers/index.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/customers/login.ts b/framework/shopify/api/customers/login.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/customers/login.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/customers/logout.ts b/framework/shopify/api/customers/logout.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/customers/logout.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/customers/signup.ts b/framework/shopify/api/customers/signup.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/customers/signup.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts new file mode 100644 index 000000000..dcb0fc2ba --- /dev/null +++ b/framework/shopify/api/index.ts @@ -0,0 +1,60 @@ +import type { CommerceAPIConfig } from '@commerce/api' +import fetchGraphqlApi from './utils/fetch-graphql-api' + +export interface ShopifyConfig extends CommerceAPIConfig {} + +// No I don't like this - will fix it later +const API_URL = + process.env.SHOPIFY_STORE_DOMAIN || + process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN +const API_TOKEN = + process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN || + process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN + +if (!API_URL) { + throw new Error( + `The environment variable SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` + ) +} + +if (!API_TOKEN) { + throw new Error( + `The environment variable SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store` + ) +} + +export class Config { + private config: ShopifyConfig + + constructor(config: ShopifyConfig) { + this.config = config + } + + getConfig(userConfig: Partial = {}) { + return Object.entries(userConfig).reduce( + (cfg, [key, value]) => Object.assign(cfg, { [key]: value }), + { ...this.config } + ) + } + + setConfig(newConfig: Partial) { + Object.assign(this.config, newConfig) + } +} + +const config = new Config({ + commerceUrl: API_URL, + apiToken: API_TOKEN, + // TODO + // @ts-ignore + fetch: fetchGraphqlApi, + customerCookie: 'SHOP_TOKEN', +}) + +export function getConfig(userConfig?: Partial) { + return config.getConfig(userConfig) +} + +export function setConfig(newConfig: Partial) { + return config.setConfig(newConfig) +} diff --git a/framework/shopify/api/operations/get-all-collections.ts b/framework/shopify/api/operations/get-all-collections.ts new file mode 100644 index 000000000..9cf216a91 --- /dev/null +++ b/framework/shopify/api/operations/get-all-collections.ts @@ -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 diff --git a/framework/shopify/api/operations/get-page.ts b/framework/shopify/api/operations/get-page.ts new file mode 100644 index 000000000..11651e335 --- /dev/null +++ b/framework/shopify/api/operations/get-page.ts @@ -0,0 +1,27 @@ +import { ShopifyConfig, getConfig } from '..' +import type { Page } from '../../types' + +export type { Page } + +export type GetPageResult = T + +export type PageVariables = { + id: string +} + +async function getPage({ + url, + variables, + config, + preview, +}: { + url?: string + variables: PageVariables + config?: ShopifyConfig + preview?: boolean +}): Promise { + config = getConfig(config) + return {} +} + +export default getPage diff --git a/framework/shopify/api/utils/fetch-graphql-api.ts b/framework/shopify/api/utils/fetch-graphql-api.ts new file mode 100644 index 000000000..946242c93 --- /dev/null +++ b/framework/shopify/api/utils/fetch-graphql-api.ts @@ -0,0 +1,51 @@ +import { CommerceAPIFetchOptions } from '@commerce/api' +import { FetcherError } from '@commerce/utils/errors' +import { getConfig } from '../index' + +export interface GraphQLFetcherResult { + data: Data + res: Response +} +export type GraphQLFetcher< + Data extends GraphQLFetcherResult = GraphQLFetcherResult, + Variables = any +> = ( + query: string, + queryData?: CommerceAPIFetchOptions, + fetchOptions?: RequestInit +) => Promise + +const fetchGraphqlApi: GraphQLFetcher = async ( + query: string, + { variables } = {}, + fetchOptions +) => { + const config = getConfig() + const url = `https://${config.commerceUrl}/api/2020-10/graphql.json` + + const res = await fetch(url, { + ...fetchOptions, + method: 'POST', + headers: { + 'X-Shopify-Storefront-Access-Token': config.apiToken, + ...fetchOptions?.headers, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query, + variables, + }), + }) + + const json = await res.json() + if (json.errors) { + throw new FetcherError({ + errors: json.errors ?? [{ message: 'Failed to fetch Shopify API' }], + status: res.status, + }) + } + + return { data: json.data, res } +} + +export default fetchGraphqlApi diff --git a/framework/shopify/api/wishlist/index.tsx b/framework/shopify/api/wishlist/index.tsx new file mode 100644 index 000000000..a72856673 --- /dev/null +++ b/framework/shopify/api/wishlist/index.tsx @@ -0,0 +1,2 @@ +export type WishlistItem = { product: any; id: number } +export default function () {} diff --git a/framework/shopify/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx new file mode 100644 index 000000000..75f067c3a --- /dev/null +++ b/framework/shopify/auth/use-login.tsx @@ -0,0 +1,13 @@ +import { useCallback } from 'react' + +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/shopify/auth/use-logout.tsx b/framework/shopify/auth/use-logout.tsx new file mode 100644 index 000000000..75f067c3a --- /dev/null +++ b/framework/shopify/auth/use-logout.tsx @@ -0,0 +1,13 @@ +import { useCallback } from 'react' + +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/shopify/auth/use-signup.tsx b/framework/shopify/auth/use-signup.tsx new file mode 100644 index 000000000..75f067c3a --- /dev/null +++ b/framework/shopify/auth/use-signup.tsx @@ -0,0 +1,13 @@ +import { useCallback } from 'react' + +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/shopify/cart/index.ts b/framework/shopify/cart/index.ts new file mode 100644 index 000000000..e1c6ef823 --- /dev/null +++ b/framework/shopify/cart/index.ts @@ -0,0 +1,5 @@ +export { default as useCart } from './use-cart' +export { default as useAddItem } from './use-add-item' +export { default as useRemoveItem } from './use-remove-item' +// export { default as useWishlistActions } from './use-cart-actions' +// export { default as useUpdateItem } from './use-cart-actions' diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx new file mode 100644 index 000000000..276d66e30 --- /dev/null +++ b/framework/shopify/cart/use-add-item.tsx @@ -0,0 +1,30 @@ +import { useCallback } from 'react' +import { LineItemToAdd } from 'shopify-buy' +import { useCommerce } from '../index' + +type Options = { + productId: number + variantId: string | number +} + +const useAddItem = () => { + const { checkout, client, updateCheckout } = useCommerce() + + return useCallback( + async function addItem(options: Options) { + const lineItems: LineItemToAdd[] = [ + { + variantId: `${options.variantId}`, + quantity: 1, + }, + ] + + const cart = await client?.checkout.addLineItems(checkout.id, lineItems) + updateCheckout(cart) + return cart + }, + [checkout, client] + ) +} + +export default useAddItem diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx new file mode 100644 index 000000000..f067b520d --- /dev/null +++ b/framework/shopify/cart/use-cart.tsx @@ -0,0 +1,42 @@ +import { useCommerce } from '../index' + +export function emptyHook() { + const { checkout } = useCommerce() + const { lineItems, totalPriceV2 } = checkout || {} + + return { + data: { + subTotal: totalPriceV2?.amount || 0, + total: totalPriceV2?.amount || 0, + currency: { + code: '', + }, + line_items: + lineItems?.map((item) => { + return [ + { + id: item.id, + name: item.title, + quantity: item.quantity, + }, + ] + }) || [], + items: + lineItems?.map((item) => { + return { + id: item.id, + name: item.title, + images: [{ url: '/jacket.png' }], + url: '/', + quantity: item.quantity, + productId: item.id, + variantId: item.id, + } + }) || [], + }, + isEmpty: false, + isLoading: false, + } +} + +export default emptyHook diff --git a/framework/shopify/cart/use-remove-item.tsx b/framework/shopify/cart/use-remove-item.tsx new file mode 100644 index 000000000..c0ce93bd5 --- /dev/null +++ b/framework/shopify/cart/use-remove-item.tsx @@ -0,0 +1,17 @@ +import { useCallback } from 'react' +import { useCommerce } from '../index' + +const useRemoveItem = () => { + const { checkout, client, updateCheckout } = useCommerce() + + return useCallback( + async function removeItem({ id }: { id: string }) { + const cart = await client?.checkout.removeLineItems(checkout.id, [id]) + updateCheckout(cart) + return cart + }, + [checkout, client] + ) +} + +export default useRemoveItem diff --git a/framework/shopify/cart/use-update-item.tsx b/framework/shopify/cart/use-update-item.tsx new file mode 100644 index 000000000..05118a65b --- /dev/null +++ b/framework/shopify/cart/use-update-item.tsx @@ -0,0 +1,24 @@ +import { useCallback } from 'react' +import { useCommerce } from '../index' + +const useUpdateItem = (item: CartItem) => { + const { checkout, client, updateCheckout } = useCommerce() + + return useCallback( + async function updateItem({ quantity }: { quantity: number }) { + const lineItemsToUpdate = [{ id: item.id, quantity }] + + const cart = await client?.checkout.updateLineItems( + checkout.id, + lineItemsToUpdate + ) + + updateCheckout(cart) + + return cart + }, + [checkout, client] + ) +} + +export default useUpdateItem diff --git a/framework/shopify/common/get-all-pages.ts b/framework/shopify/common/get-all-pages.ts new file mode 100644 index 000000000..02db3fdc3 --- /dev/null +++ b/framework/shopify/common/get-all-pages.ts @@ -0,0 +1,55 @@ +import { getConfig, ShopifyConfig } from '../api' +import { Page as PageType, PageEdge } from '../types' + +export type Page = PageType + +export const getAllPagesQuery = /* GraphQL */ ` + query($first: Int!) { + pages(first: $first) { + edges { + node { + id + title + handle + body + bodySummary + url + } + } + } + } +` + +type Variables = { + first?: number +} + +type Options = { + variables?: Variables + config: ShopifyConfig + preview?: boolean +} + +type ReturnType = { + pages: Page[] +} + +const getAllPages = async (options?: Options): Promise => { + let { config, variables = { first: 250 } } = options || {} + + config = getConfig(config) + + const { data } = await config.fetch(getAllPagesQuery, { variables }) + + const pages = data.pages.edges.map(({ node }: PageEdge) => { + return { + ...node, + name: node.handle, + url: `${config!.locale}/${node.handle}`, + } + }) + + return { pages } +} + +export default getAllPages diff --git a/framework/shopify/common/get-site-info.ts b/framework/shopify/common/get-site-info.ts new file mode 100644 index 000000000..c08ae2b92 --- /dev/null +++ b/framework/shopify/common/get-site-info.ts @@ -0,0 +1,30 @@ +import { ShopifyConfig } from '../index' + +type Options = { + config: ShopifyConfig + preview?: boolean +} + +const getSiteInfo = async (options: Options) => { + // TODO + return { + categories: [ + { + path: '', + name: '', + entityId: 0, + }, + ], + brands: [ + { + node: { + path: '', + name: '', + entityId: 0, + }, + }, + ], + } +} + +export default getSiteInfo diff --git a/framework/shopify/customer/use-customer.tsx b/framework/shopify/customer/use-customer.tsx new file mode 100644 index 000000000..a909443ff --- /dev/null +++ b/framework/shopify/customer/use-customer.tsx @@ -0,0 +1,32 @@ +import type { HookFetcher } from '@commerce/utils/types' +import type { SwrOptions } from '@commerce/utils/use-data' +import useCommerceCustomer from '@commerce/use-customer' + +const defaultOpts = {} + +export type Customer = { + entityId: number + firstName: string + lastName: string + email: string +} +export type CustomerData = {} + +export const fetcher: HookFetcher = async () => { + return null +} + +export function extendHook( + customFetcher: typeof fetcher, + swrOptions?: SwrOptions +) { + const useCustomer = () => { + return { data: { firstName: null, lastName: null, email: null } } + } + + useCustomer.extend = extendHook + + return useCustomer +} + +export default extendHook(fetcher) diff --git a/framework/shopify/index.tsx b/framework/shopify/index.tsx new file mode 100644 index 000000000..5fd08e0d9 --- /dev/null +++ b/framework/shopify/index.tsx @@ -0,0 +1,109 @@ +import React, { + ReactNode, + createContext, + useContext, + useMemo, + useState, + useEffect, +} from 'react' +import Client from 'shopify-buy' +import { Shop, Cart, Client as ClientType } from './types' +import { + getCheckoutIdFromStorage, + setCheckoutIdInStorage, +} from './utils/storage' +import { getConfig } from '@framework/api' + +const Commerce = createContext({}) + +type CommerceProps = { + children?: ReactNode + locale: string +} + +type CommerceContextValue = { + client: ClientType + shop: Shop + checkout: Cart + updateCheckout: (cart: Cart | undefined) => void + currencyCode: string + locale: string + sessionToken: string +} + +export function CommerceProvider({ + children, + locale = 'en-US', +}: CommerceProps) { + const sessionToken = 'nextjs-commerce-shopify-token' + + const config = getConfig() + + const client = Client.buildClient({ + storefrontAccessToken: config.apiToken, + domain: config.commerceUrl, + language: locale, + }) as ClientType + + const [shop, setShop] = useState() + const [checkout, setCheckout] = useState() + + const fetchShopify = async () => { + const shopInfo: Shop = await client.shop.fetchInfo() + let checkoutResource: Cart + + const checkoutOptions = { + presentmentCurrencyCode: + /*config.currencyCode ||*/ shopInfo?.currencyCode, + } + + let checkoutId = getCheckoutIdFromStorage(sessionToken) + + // we could have a cart id stored in session storage + // user could be refreshing or navigating back and forth + if (checkoutId) { + checkoutResource = await client.checkout.fetch(checkoutId) + + // could be expired order - we will create a new order + if (checkoutResource.completedAt) { + checkoutResource = await client.checkout.create(checkoutOptions) + } + } else { + checkoutResource = await client.checkout.create(checkoutOptions) + } + + setCheckoutIdInStorage(sessionToken, checkoutResource.id) + + setShop(shopInfo) + setCheckout(checkoutResource) + } + + useEffect(() => { + fetchShopify() + }, []) + + const updateCheckout = (newCheckout: Cart) => { + setCheckout(newCheckout) + } + + // Because the config is an object, if the parent re-renders this provider + // will re-render every consumer unless we memoize the config + const cfg = useMemo( + () => ({ + client, + checkout, + shop, + updateCheckout: updateCheckout, + currencyCode: /*config.currencyCode ||*/ checkout?.currencyCode, + locale, + sessionToken, + }), + [client] + ) + + return {children} +} + +export function useCommerce() { + return useContext(Commerce) as T +} diff --git a/framework/shopify/product/get-all-product-paths.ts b/framework/shopify/product/get-all-product-paths.ts new file mode 100644 index 000000000..3d4f0ef7a --- /dev/null +++ b/framework/shopify/product/get-all-product-paths.ts @@ -0,0 +1,31 @@ +import Client from 'shopify-buy' +import { getConfig } from '../api' +import { Product } from '../types' +import toCommerceProducts from '../utils/to-commerce-products' + +type ReturnType = { + products: any[] +} + +const getAllProductPaths = async (): Promise => { + const config = getConfig() + + const client = Client.buildClient({ + storefrontAccessToken: config.apiToken, + domain: config.commerceUrl, + }) + + const res = (await client.product.fetchAll()) as Product[] + + const products = toCommerceProducts(res) + + return { + products: products.map((product) => { + return { + node: { ...product }, + } + }), + } +} + +export default getAllProductPaths diff --git a/framework/shopify/product/get-all-products.ts b/framework/shopify/product/get-all-products.ts new file mode 100644 index 000000000..6e4881e99 --- /dev/null +++ b/framework/shopify/product/get-all-products.ts @@ -0,0 +1,40 @@ +import Client from 'shopify-buy' +import { ShopifyConfig } from '../api' +import { Product } from '../types' +import toCommerceProducts from '../utils/to-commerce-products' + +export type ProductNode = Product + +type Variables = { + first?: number + field?: string +} + +type Options = { + variables: Variables + config: ShopifyConfig + preview?: boolean +} + +type ReturnType = { + products: any[] +} + +const getAllProducts = async (options: Options): Promise => { + const { config } = options + + const client = Client.buildClient({ + storefrontAccessToken: config.apiToken, + domain: config.commerceUrl, + }) + + const res = (await client.product.fetchAll()) as Product[] + + const products = toCommerceProducts(res) + + return { + products, + } +} + +export default getAllProducts diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts new file mode 100644 index 000000000..f71aa0213 --- /dev/null +++ b/framework/shopify/product/get-product.ts @@ -0,0 +1,37 @@ +import Client from 'shopify-buy' +import { ShopifyConfig } from '../api' +import { Product } from '../types' +import toCommerceProducts from '../utils/to-commerce-products' + +export type ProductNode = Product + +type Variables = { + slug: string +} + +type Options = { + variables: Variables + config: ShopifyConfig + preview?: boolean +} + +type ReturnType = { + product: any +} + +const getProduct = async (options: Options): Promise => { + const { variables, config } = options + + const client = Client.buildClient({ + storefrontAccessToken: config.apiToken, + domain: config.commerceUrl, + }) + + const res = (await client.product.fetchByHandle(variables.slug)) as Product + + return { + product: toCommerceProducts([res])[0], + } +} + +export default getProduct diff --git a/framework/shopify/product/use-price.tsx b/framework/shopify/product/use-price.tsx new file mode 100644 index 000000000..a79940a76 --- /dev/null +++ b/framework/shopify/product/use-price.tsx @@ -0,0 +1,2 @@ +export * from '@commerce/use-price' +export { default } from '@commerce/use-price' diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx new file mode 100644 index 000000000..a2c32c896 --- /dev/null +++ b/framework/shopify/product/use-search.tsx @@ -0,0 +1,41 @@ +import type { HookFetcher } from '@commerce/utils/types' +import type { SwrOptions } from '@commerce/utils/use-data' +import useCommerceSearch from '@commerce/products/use-search' +import { ProductEdge } from '../types' + +const defaultOpts = {} + +export type SearchProductsInput = { + search?: string + categoryId?: number + brandId?: number + sort?: string +} + +export type SearchProductsData = { + products: ProductEdge[] + found: boolean +} + +export const fetcher: HookFetcher = ( + options, + { search, categoryId, brandId, sort }, + fetch +) => { + return { found: false, products: [] } +} + +export function extendHook( + customFetcher: typeof fetcher, + swrOptions?: SwrOptions +) { + const useSearch = (input: SearchProductsInput = {}) => { + return {} + } + + useSearch.extend = extendHook + + return useSearch +} + +export default extendHook(fetcher) diff --git a/framework/shopify/types.ts b/framework/shopify/types.ts new file mode 100644 index 000000000..47bb94e62 --- /dev/null +++ b/framework/shopify/types.ts @@ -0,0 +1,130 @@ +import { + Product as BaseProduct, + ProductVariant as BaseProductVariant, + Cart as BaseCart, + CheckoutResource as BaseCheckoutResource, + AttributeInput, + Client as BaseClient, + Shop as BaseShop, + Image as BaseImage, +} from 'shopify-buy' + +export type SelectedOptions = { + id: string + name: string + value: string +} + +export type PresentmentPrice = { + price: PriceV2 +} + +export type ProductVariant = BaseProductVariant & { + selectedOptions: Array + presentmentPrices: Array +} + +// TODO +export type ProductOptions = { + node: { + __typename: string + displayName: string + values: { + edges: [ + { + node: { + label: string + id: string + } + } + ] + } + } +} + +// TODO +export type ProductEdge = { + node: Product +} + +export type Product = BaseProduct & { + handle: string + name: string + path: string + entityId: number + descriptionHtml: string + prices: { + price: { + value: number + currencyCode: string + } + retailPrice: { + value: number + currencyCode: string + } + } + images: { + edges: [{ node: { urlOriginal: string; altText: string } }] + } + productOptions: ProductOptions + variants: Array & { + edges: [ + { + node: { + productOptions: ProductOptions[] + entityId: number + } + } + ] + } +} + +export type PriceV2 = { + amount: number + currencyCode: string +} + +export type Cart = BaseCart & { + webUrl?: string + currencyCode?: string + lineItemsSubtotalPrice?: PriceV2 + totalPriceV2?: PriceV2 +} + +export type Shop = BaseShop & { + currencyCode?: string +} + +export type Create = { + presentmentCurrencyCode?: string +} + +export type CheckoutResource = BaseCheckoutResource & { + updateLineItems( + checkoutId: string | number, + lineItems: AttributeInput[] + ): Promise + + create: (input: Create) => Promise +} + +export type Client = BaseClient & { + checkout: CheckoutResource +} + +export type Page = { + id: string + title: string + name: string + handle: string + body: string + bodySummary: string + url: string + sort_order: number +} + +export type PageEdge = { + node: Page +} + +export type Image = BaseImage diff --git a/framework/shopify/utils/storage.ts b/framework/shopify/utils/storage.ts new file mode 100644 index 000000000..d46dadb21 --- /dev/null +++ b/framework/shopify/utils/storage.ts @@ -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 + '') + } +} diff --git a/framework/shopify/utils/to-commerce-products.ts b/framework/shopify/utils/to-commerce-products.ts new file mode 100644 index 000000000..c0b411eb6 --- /dev/null +++ b/framework/shopify/utils/to-commerce-products.ts @@ -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: [], + } + }) +} diff --git a/framework/shopify/wishlist/use-add-item.tsx b/framework/shopify/wishlist/use-add-item.tsx new file mode 100644 index 000000000..75f067c3a --- /dev/null +++ b/framework/shopify/wishlist/use-add-item.tsx @@ -0,0 +1,13 @@ +import { useCallback } from 'react' + +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/shopify/wishlist/use-remove-item.tsx b/framework/shopify/wishlist/use-remove-item.tsx new file mode 100644 index 000000000..a2d3a8a05 --- /dev/null +++ b/framework/shopify/wishlist/use-remove-item.tsx @@ -0,0 +1,17 @@ +import { useCallback } from 'react' + +type Options = { + includeProducts?: boolean +} + +export function emptyHook(options?: Options) { + const useEmptyHook = async ({ id }: { id: string | number }) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/shopify/wishlist/use-wishlist.tsx b/framework/shopify/wishlist/use-wishlist.tsx new file mode 100644 index 000000000..2aac16810 --- /dev/null +++ b/framework/shopify/wishlist/use-wishlist.tsx @@ -0,0 +1,45 @@ +import { HookFetcher } from '@commerce/utils/types' +import { SwrOptions } from '@commerce/utils/use-data' +import useCommerceWishlist from '@commerce/wishlist/use-wishlist' +import { Product } from '../types' +import useCustomer from '../customer/use-customer' + +const defaultOpts = {} + +export type Wishlist = { + items: [ + { + product_id: number + variant_id: number + id: number + product: Product + } + ] +} + +export interface UseWishlistOptions { + includeProducts?: boolean +} + +export interface UseWishlistInput extends UseWishlistOptions { + customerId?: number +} + +export const fetcher: HookFetcher = () => { + return null +} + +export function extendHook( + customFetcher: typeof fetcher, + swrOptions?: SwrOptions +) { + const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => { + return { data: null } + } + + useWishlist.extend = extendHook + + return useWishlist +} + +export default extendHook(fetcher) diff --git a/next.config.js b/next.config.js index e732ef78a..3c9e37210 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,6 @@ module.exports = { images: { - domains: ['cdn11.bigcommerce.com'], + domains: ['cdn11.bigcommerce.com', 'cdn.shopify.com'], }, i18n: { locales: ['en-US', 'es'], From a8607f24cd215129527418dc6b81748baa1faae4 Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Mon, 22 Feb 2021 19:06:03 -0500 Subject: [PATCH 04/11] Updates to wishlist feature --- components/common/Layout/Layout.tsx | 5 ++--- components/product/ProductCard/ProductCard.tsx | 4 ++-- components/product/ProductView/ProductView.tsx | 4 ++-- components/wishlist/WishlistButton/WishlistButton.tsx | 5 +++-- framework/commerce/utils/features.ts | 2 +- next.config.js | 3 +++ package.json | 1 + pages/index.tsx | 8 ++++---- pages/product/[slug].tsx | 2 +- pages/search.tsx | 1 + 10 files changed, 20 insertions(+), 15 deletions(-) diff --git a/components/common/Layout/Layout.tsx b/components/common/Layout/Layout.tsx index f4376bbf3..82e045474 100644 --- a/components/common/Layout/Layout.tsx +++ b/components/common/Layout/Layout.tsx @@ -58,11 +58,10 @@ const Layout: FC = ({ } = useUI() const { acceptedCookies, onAcceptCookies } = useAcceptCookies() const { locale = 'en-US' } = useRouter() - const isWishlistEnabled = commerceFeatures.wishlist return (
- +
{children}
@@ -73,7 +72,7 @@ const Layout: FC = ({ - + = ({ {product.price.currencyCode}
- {wishlist && ( + {process.env.WISHLIST_ENABLED && ( )}
diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index c502303c4..e666e1a08 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -152,11 +152,11 @@ const ProductView: FC = ({ product, wishlist = false }) => {
- {wishlist && ( + {process.env.WISHLIST_ENABLED && ( )} diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index 0c4c20194..e22215363 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -7,7 +7,7 @@ import type { Product, ProductVariant } from '@commerce/types' import useCustomer from '@framework/customer/use-customer' import useAddItem from '@framework/wishlist/use-add-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 = { productId: Product['id'] @@ -28,7 +28,8 @@ const WishlistButton: FC = ({ 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) => { diff --git a/framework/commerce/utils/features.ts b/framework/commerce/utils/features.ts index d84321967..98a53ed54 100644 --- a/framework/commerce/utils/features.ts +++ b/framework/commerce/utils/features.ts @@ -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' diff --git a/next.config.js b/next.config.js index e732ef78a..939031884 100644 --- a/next.config.js +++ b/next.config.js @@ -6,6 +6,9 @@ module.exports = { locales: ['en-US', 'es'], defaultLocale: 'en-US', }, + env: { + WISHLIST_ENABLED: false, + }, rewrites() { return [ { diff --git a/package.json b/package.json index 2d8e32772..d1bfbb574 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "generate": "graphql-codegen", "generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js" }, + "sideEffects": false, "license": "MIT", "engines": { "node": "12.x" diff --git a/pages/index.tsx b/pages/index.tsx index acb1474be..c4fb68252 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -57,7 +57,7 @@ export default function Home({ width: i === 0 ? 1080 : 540, height: i === 0 ? 1080 : 540, }} - wishlist={commerceFeatures.wishlist} + wishlist={!!process.env.WISHLIST_ENABLED} /> ))} @@ -71,7 +71,7 @@ export default function Home({ width: 320, height: 320, }} - wishlist={commerceFeatures.wishlist} + wishlist={!!process.env.WISHLIST_ENABLED} /> ))} @@ -94,7 +94,7 @@ export default function Home({ width: i === 0 ? 1080 : 540, height: i === 0 ? 1080 : 540, }} - wishlist={commerceFeatures.wishlist} + wishlist={!!process.env.WISHLIST_ENABLED} /> ))} @@ -108,7 +108,7 @@ export default function Home({ width: 320, height: 320, }} - wishlist={commerceFeatures.wishlist} + wishlist={!!process.env.WISHLIST_ENABLED} /> ))} diff --git a/pages/product/[slug].tsx b/pages/product/[slug].tsx index a705c001b..90da202b2 100644 --- a/pages/product/[slug].tsx +++ b/pages/product/[slug].tsx @@ -71,7 +71,7 @@ export default function Slug({ ) : ( ) } diff --git a/pages/search.tsx b/pages/search.tsx index c9958a9f8..d62ce22d4 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -34,6 +34,7 @@ import { getDesignerPath, useSearchMeta, } from '@lib/search' +import { Product } from '@commerce/types' export async function getStaticProps({ preview, From 67d05ea165c22a8cedf98b38d73ca9593b73a70b Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Tue, 23 Feb 2021 11:32:54 -0500 Subject: [PATCH 05/11] Moved the features to be environment variable only --- components/product/ProductCard/ProductCard.tsx | 2 -- components/product/ProductView/ProductView.tsx | 3 +-- next.config.js | 2 +- pages/index.tsx | 10 ---------- pages/product/[slug].tsx | 11 +---------- pages/search.tsx | 8 -------- pages/wishlist.tsx | 3 +-- 7 files changed, 4 insertions(+), 35 deletions(-) diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 52ce129dd..3c28e6663 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -11,7 +11,6 @@ interface Props { product: Product variant?: 'slim' | 'simple' imgProps?: Omit - wishlist?: boolean } const placeholderImg = '/product-img-placeholder.svg' @@ -21,7 +20,6 @@ const ProductCard: FC = ({ product, variant, imgProps, - wishlist = false, ...props }) => ( diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index e666e1a08..51b9888b0 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -19,10 +19,9 @@ interface Props { className?: string children?: any product: Product - wishlist?: boolean } -const ProductView: FC = ({ product, wishlist = false }) => { +const ProductView: FC = ({ product }) => { const addItem = useAddItem() const { price } = usePrice({ amount: product.price.value, diff --git a/next.config.js b/next.config.js index 939031884..fe4441dda 100644 --- a/next.config.js +++ b/next.config.js @@ -7,7 +7,7 @@ module.exports = { defaultLocale: 'en-US', }, env: { - WISHLIST_ENABLED: false, + WISHLIST_ENABLED: true, }, rewrites() { return [ diff --git a/pages/index.tsx b/pages/index.tsx index c4fb68252..3a84112e5 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -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) { return ( <> @@ -57,7 +51,6 @@ export default function Home({ width: i === 0 ? 1080 : 540, height: i === 0 ? 1080 : 540, }} - wishlist={!!process.env.WISHLIST_ENABLED} /> ))} @@ -71,7 +64,6 @@ export default function Home({ width: 320, height: 320, }} - wishlist={!!process.env.WISHLIST_ENABLED} /> ))} @@ -94,7 +86,6 @@ export default function Home({ width: i === 0 ? 1080 : 540, height: i === 0 ? 1080 : 540, }} - wishlist={!!process.env.WISHLIST_ENABLED} /> ))} @@ -108,7 +99,6 @@ export default function Home({ width: 320, height: 320, }} - wishlist={!!process.env.WISHLIST_ENABLED} /> ))} diff --git a/pages/product/[slug].tsx b/pages/product/[slug].tsx index 90da202b2..61420a8d9 100644 --- a/pages/product/[slug].tsx +++ b/pages/product/[slug].tsx @@ -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) { const router = useRouter() return router.isFallback ? (

Loading...

// TODO (BC) Add Skeleton Views ) : ( - + ) } diff --git a/pages/search.tsx b/pages/search.tsx index d62ce22d4..a05203892 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -26,8 +26,6 @@ const SORT = Object.entries({ 'price-desc': 'Price: High to low', }) -import Features from '@commerce/utils/features' - import { filterQuery, getCategoryPath, @@ -43,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, - }, }, } } @@ -59,7 +53,6 @@ export async function getStaticProps({ export default function Search({ categories, brands, - commerceFeatures: { wishlist }, }: InferGetStaticPropsType) { const [activeFilter, setActiveFilter] = useState('') const [toggleFilter, setToggleFilter] = useState(false) @@ -359,7 +352,6 @@ export default function Search({ width: 480, height: 480, }} - wishlist={wishlist} /> ))} diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index ca11152f4..dcda912c6 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -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.WISHLIST_ENABLED) { return { notFound: true, } From 412f2681487dd46917197172221d442dd12cddcc Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Tue, 23 Feb 2021 13:21:25 -0500 Subject: [PATCH 06/11] More changes for wishlist config --- components/cart/CartSidebarView/CartSidebarView.tsx | 4 ++-- .../common/HomeAllProductsGrid/HomeAllProductsGrid.tsx | 4 ---- components/common/Layout/Layout.tsx | 4 ++-- components/common/Navbar/Navbar.tsx | 4 ++-- components/common/UserNav/UserNav.tsx | 5 ++--- 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 5b28fde27..3ceda44fe 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -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 }) => {
- +
diff --git a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx index 4b838e1a4..423048f75 100644 --- a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx +++ b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx @@ -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 = ({ categories, brands, products = [], - wishlist = false, }) => { return (
@@ -65,7 +62,6 @@ const HomeAllProductsGrid: FC = ({ width: 480, height: 480, }} - wishlist={wishlist} /> ))} diff --git a/components/common/Layout/Layout.tsx b/components/common/Layout/Layout.tsx index 82e045474..54749c46b 100644 --- a/components/common/Layout/Layout.tsx +++ b/components/common/Layout/Layout.tsx @@ -61,7 +61,7 @@ const Layout: FC = ({ return (
- +
{children}
@@ -72,7 +72,7 @@ const Layout: FC = ({ - + = ({ wishlist }) => ( +const Navbar: FC = () => (
@@ -33,7 +33,7 @@ const Navbar: FC<{ wishlist?: boolean }> = ({ wishlist }) => (
- +
diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index 5d9d58fff..d2dfc11dd 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -12,12 +12,11 @@ import { Avatar } from '@components/common' interface Props { className?: string - wishlist?: boolean } const countItem = (count: number, item: LineItem) => count + item.quantity -const UserNav: FC = ({ className, wishlist = false }) => { +const UserNav: FC = ({ className }) => { const { data } = useCart() const { data: customer } = useCustomer() const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI() @@ -31,7 +30,7 @@ const UserNav: FC = ({ className, wishlist = false }) => { {itemsCount > 0 && {itemsCount}} - {wishlist && ( + {process.env.WISHLIST_ENABLED && (
  • From b931bc47afe20c923381316d36c6e3b4a4358bc7 Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Tue, 23 Feb 2021 13:59:11 -0500 Subject: [PATCH 07/11] Disable wishlist --- next.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/next.config.js b/next.config.js index fe4441dda..939031884 100644 --- a/next.config.js +++ b/next.config.js @@ -7,7 +7,7 @@ module.exports = { defaultLocale: 'en-US', }, env: { - WISHLIST_ENABLED: true, + WISHLIST_ENABLED: false, }, rewrites() { return [ From 9b251a10c6b844af7c2078b08bddc66fd1019e34 Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Tue, 23 Feb 2021 23:03:23 -0500 Subject: [PATCH 08/11] Removed useWishlistActions --- framework/bigcommerce/wishlist/index.ts | 1 - .../bigcommerce/wishlist/use-wishlist-actions.tsx | 11 ----------- 2 files changed, 12 deletions(-) delete mode 100644 framework/bigcommerce/wishlist/use-wishlist-actions.tsx diff --git a/framework/bigcommerce/wishlist/index.ts b/framework/bigcommerce/wishlist/index.ts index 9ea28291c..241af3c7e 100644 --- a/framework/bigcommerce/wishlist/index.ts +++ b/framework/bigcommerce/wishlist/index.ts @@ -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' diff --git a/framework/bigcommerce/wishlist/use-wishlist-actions.tsx b/framework/bigcommerce/wishlist/use-wishlist-actions.tsx deleted file mode 100644 index 711d00516..000000000 --- a/framework/bigcommerce/wishlist/use-wishlist-actions.tsx +++ /dev/null @@ -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 } -} From bbfca29217540df58ff34f604970e2bb16e856d4 Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Tue, 23 Feb 2021 23:07:49 -0500 Subject: [PATCH 09/11] Updated readme --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ea6248c51..9d2108cfe 100644 --- a/README.md +++ b/README.md @@ -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` From bcd1d46e30d0cfc7078544afcf82532d03b586d6 Mon Sep 17 00:00:00 2001 From: okbel Date: Wed, 24 Feb 2021 14:37:25 -0300 Subject: [PATCH 10/11] updates --- .vscode/extensions.json | 3 ++ components/common/UserNav/UserNav.tsx | 2 +- .../product/ProductCard/ProductCard.tsx | 2 +- .../product/ProductView/ProductView.tsx | 2 +- framework/bigcommerce/auth/use-login.tsx | 2 +- framework/bigcommerce/auth/use-logout.tsx | 2 +- framework/bigcommerce/auth/use-signup.tsx | 2 +- framework/bigcommerce/next.config.js | 37 +++++++++++++++ framework/bigcommerce/product/use-price.tsx | 4 +- framework/commerce/{ => auth}/use-login.tsx | 8 ++-- framework/commerce/{ => auth}/use-logout.tsx | 8 ++-- framework/commerce/{ => auth}/use-signup.tsx | 8 ++-- framework/commerce/config.json | 5 -- .../commerce/{ => product}/use-price.tsx | 2 +- framework/commerce/types.ts | 4 +- framework/commerce/utils/bootstrap.js | 11 +++++ framework/commerce/utils/features.ts | 10 ++++ next.config.js | 46 +++---------------- package.json | 1 + pages/wishlist.tsx | 2 +- yarn.lock | 5 ++ 21 files changed, 98 insertions(+), 68 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 framework/bigcommerce/next.config.js rename framework/commerce/{ => auth}/use-login.tsx (64%) rename framework/commerce/{ => auth}/use-logout.tsx (64%) rename framework/commerce/{ => auth}/use-signup.tsx (64%) delete mode 100644 framework/commerce/config.json rename framework/commerce/{ => product}/use-price.tsx (97%) create mode 100644 framework/commerce/utils/bootstrap.js diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..c83e26348 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["esbenp.prettier-vscode"] +} diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index d2dfc11dd..9d143a572 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -30,7 +30,7 @@ const UserNav: FC = ({ className }) => { {itemsCount > 0 && {itemsCount}}
  • - {process.env.WISHLIST_ENABLED && ( + {process.env.COMMERCE_WISHLIST_ENABLED && (
  • diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 3c28e6663..ade53380c 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -57,7 +57,7 @@ const ProductCard: FC = ({ {product.price.currencyCode}
  • - {process.env.WISHLIST_ENABLED && ( + {process.env.COMMERCE_WISHLIST_ENABLED && ( = ({ product }) => { - {process.env.WISHLIST_ENABLED && ( + {process.env.COMMERCE_WISHLIST_ENABLED && ( diff --git a/framework/bigcommerce/auth/use-signup.tsx b/framework/bigcommerce/auth/use-signup.tsx index 23b7ce9c6..28f7024ef 100644 --- a/framework/bigcommerce/auth/use-signup.tsx +++ b/framework/bigcommerce/auth/use-signup.tsx @@ -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' diff --git a/framework/bigcommerce/next.config.js b/framework/bigcommerce/next.config.js new file mode 100644 index 000000000..e732ef78a --- /dev/null +++ b/framework/bigcommerce/next.config.js @@ -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', + }, + ] + }, +} diff --git a/framework/bigcommerce/product/use-price.tsx b/framework/bigcommerce/product/use-price.tsx index a79940a76..0174faf5e 100644 --- a/framework/bigcommerce/product/use-price.tsx +++ b/framework/bigcommerce/product/use-price.tsx @@ -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' diff --git a/framework/commerce/use-login.tsx b/framework/commerce/auth/use-login.tsx similarity index 64% rename from framework/commerce/use-login.tsx rename to framework/commerce/auth/use-login.tsx index 755e10fd9..cc4cf6a73 100644 --- a/framework/commerce/use-login.tsx +++ b/framework/commerce/auth/use-login.tsx @@ -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 = MutationHook diff --git a/framework/commerce/use-logout.tsx b/framework/commerce/auth/use-logout.tsx similarity index 64% rename from framework/commerce/use-logout.tsx rename to framework/commerce/auth/use-logout.tsx index 0a80c318b..d0f7e3ae0 100644 --- a/framework/commerce/use-logout.tsx +++ b/framework/commerce/auth/use-logout.tsx @@ -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 = MutationHook diff --git a/framework/commerce/use-signup.tsx b/framework/commerce/auth/use-signup.tsx similarity index 64% rename from framework/commerce/use-signup.tsx rename to framework/commerce/auth/use-signup.tsx index be3c32000..72e242209 100644 --- a/framework/commerce/use-signup.tsx +++ b/framework/commerce/auth/use-signup.tsx @@ -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 = MutationHook diff --git a/framework/commerce/config.json b/framework/commerce/config.json deleted file mode 100644 index a0e7afc5d..000000000 --- a/framework/commerce/config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "features": { - "wishlist": true - } -} diff --git a/framework/commerce/use-price.tsx b/framework/commerce/product/use-price.tsx similarity index 97% rename from framework/commerce/use-price.tsx rename to framework/commerce/product/use-price.tsx index 9a3fc4b6e..9c09e3487 100644 --- a/framework/commerce/use-price.tsx +++ b/framework/commerce/product/use-price.tsx @@ -1,5 +1,5 @@ import { useMemo } from 'react' -import { useCommerce } from '.' +import { useCommerce } from '..' export function formatPrice({ amount, diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 0ae766095..57e707f35 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -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 + features: Record } export type Discount = { diff --git a/framework/commerce/utils/bootstrap.js b/framework/commerce/utils/bootstrap.js new file mode 100644 index 000000000..6a604b7dd --- /dev/null +++ b/framework/commerce/utils/bootstrap.js @@ -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 +} diff --git a/framework/commerce/utils/features.ts b/framework/commerce/utils/features.ts index 98a53ed54..72ed0d7f5 100644 --- a/framework/commerce/utils/features.ts +++ b/framework/commerce/utils/features.ts @@ -14,6 +14,16 @@ function isFeatureEnabled(config: CommerceProviderConfig) { .includes(desideredFeature) } +export function toEnvConfig( + configMap: CommerceProviderConfig['features'] +): Map { + let toEnvConfigMap = new Map() + Object.keys(configMap).map((r) => + toEnvConfigMap.set(`${r.toUpperCase()}_ENABLED`, configMap[r]) + ) + return toEnvConfigMap +} + function boostrap(): FeaturesAPI { const basis = { isEnabled: () => false, diff --git a/next.config.js b/next.config.js index 939031884..749fd1b0a 100644 --- a/next.config.js +++ b/next.config.js @@ -1,40 +1,6 @@ -module.exports = { - images: { - domains: ['cdn11.bigcommerce.com'], - }, - i18n: { - locales: ['en-US', 'es'], - defaultLocale: 'en-US', - }, - env: { - WISHLIST_ENABLED: false, - }, - 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', - }, - ] - }, -} +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)) diff --git a/package.json b/package.json index d1bfbb574..8a7d64e6a 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,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", diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index dcda912c6..ce97532b0 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -17,7 +17,7 @@ export async function getStaticProps({ locale, }: GetStaticPropsContext) { // Disabling page if Feature is not available - if (!process.env.WISHLIST_ENABLED) { + if (!process.env.COMMERCE_WISHLIST_ENABLED) { return { notFound: true, } diff --git a/yarn.lock b/yarn.lock index e7cd08438..3497602b9 100644 --- a/yarn.lock +++ b/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" 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" From 4de9b906f4f15db74a792cd1fa743f56ea92767b Mon Sep 17 00:00:00 2001 From: okbel Date: Wed, 24 Feb 2021 19:32:03 -0300 Subject: [PATCH 11/11] typos --- framework/bigcommerce/config.json | 2 +- framework/shopify/api/index.ts | 2 +- framework/shopify/customer/use-customer.tsx | 2 +- framework/shopify/product/use-search.tsx | 2 +- package.json | 1 + yarn.lock | 5 +++++ 6 files changed, 10 insertions(+), 4 deletions(-) diff --git a/framework/bigcommerce/config.json b/framework/bigcommerce/config.json index 17ef37e25..a0e7afc5d 100644 --- a/framework/bigcommerce/config.json +++ b/framework/bigcommerce/config.json @@ -1,5 +1,5 @@ { "features": { - "wishlist": false + "wishlist": true } } diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index dcb0fc2ba..0402cfccc 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -3,7 +3,7 @@ import fetchGraphqlApi from './utils/fetch-graphql-api' export interface ShopifyConfig extends CommerceAPIConfig {} -// No I don't like this - will fix it later +// TODO(bc) const API_URL = process.env.SHOPIFY_STORE_DOMAIN || process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN diff --git a/framework/shopify/customer/use-customer.tsx b/framework/shopify/customer/use-customer.tsx index a909443ff..7ea2ae679 100644 --- a/framework/shopify/customer/use-customer.tsx +++ b/framework/shopify/customer/use-customer.tsx @@ -1,6 +1,6 @@ import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' -import useCommerceCustomer from '@commerce/use-customer' +import useCommerceCustomer from '@commerce/customer/use-customer' const defaultOpts = {} diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index a2c32c896..04f6a3536 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -1,6 +1,6 @@ import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' -import useCommerceSearch from '@commerce/products/use-search' +import useCommerceSearch from '@commerce/product/use-search' import { ProductEdge } from '../types' const defaultOpts = {} diff --git a/package.json b/package.json index 8a7d64e6a..85bd9c063 100644 --- a/package.json +++ b/package.json @@ -46,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" diff --git a/yarn.lock b/yarn.lock index 3497602b9..1255b9d62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6226,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"