diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 000000000..e1076edfa
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,6 @@
+{
+ "semi": false,
+ "singleQuote": true,
+ "tabWidth": 2,
+ "useTabs": false
+}
diff --git a/README.md b/README.md
index f254b1b07..885c95e85 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,8 @@ Start right now at [nextjs.org/commerce](https://nextjs.org/commerce)
Demo live at: [demo.vercel.store](https://demo.vercel.store/)
-This project is currently under development.
+- Shopify Demo: https://shopify.demo.vercel.store/
+- BigCommerce Demo: https://bigcommerce.demo.vercel.store/
## Features
@@ -21,34 +22,22 @@ This project is currently under development.
- Integrations - Integrate seamlessly with the most common ecommerce platforms.
- Dark Mode Support
-## Work in progress
-
-We're using Github Projects to keep track of issues in progress and todo's. Here is our [Board](https://github.com/vercel/commerce/projects/1)
-
## Integrations
-Next.js Commerce integrates out-of-the-box with BigCommerce. We plan to support all major ecommerce backends.
+Next.js Commerce integrates out-of-the-box with BigCommerce and Shopify. We plan to support all major ecommerce backends.
-## Goals
+## Considerations
-- **Next.js Commerce** should have a completely data **agnostic** UI
-- **Aware of schema**: should ship with the right data schemas and types.
-- All providers should return the right data types and schemas to blend correctly with Next.js Commerce.
-- `@framework` will be the alias utilized in commerce and it will map to the ecommerce provider of preference- e.g BigCommerce, Shopify, Swell. All providers should expose the same standardized functions. _Note that the same applies for recipes using a CMS + an ecommerce provider._
+- `framework/commerce` contains all types, helpers and functions to be used as base to build a new **provider**.
+- **Providers** live under `framework`'s root folder and they will extend Next.js Commerce types and functionality.
+- **Features API** is to ensure feature parity between the UI and the Provider. The UI should update accordingly and no extra code should be bundled. All extra configuration for features will live under `features` in `commerce.config.json` and if needed it can also be accessed programatically.
+- Each **provider** should add its corresponding `next.config.js` and `commerce.config.json` adding specific data related to the provider. For example in case of BigCommerce, the images CDN and additional API routes.
+- **Providers don't depend on anything that's specific to the application they're used in**. They only depend on `framework/commerce`, on their own framework folder and on some dependencies included in `package.json`
+- We recommend that each **provider** ships with an `env.template` file and a `[readme.md](http://readme.md)` file.
-There is a `framework` folder in the root folder that will contain multiple ecommerce providers.
+## Provider Structure
-Additionally, we need to ensure feature parity (not all providers have e.g. wishlist) we will also have to build a feature API to disable/enable features in the UI.
-
-People actively working on this project: @okbel & @lfades.
-
-## Framework
-
-Framework is where the data comes from. It contains mostly hooks and functions.
-
-## Structure
-
-Main folder and its exposed functions
+Next.js Commerce provides a set of utilities and functions to create new providers. This is how a provider structure looks like.
- `product`
- usePrice
@@ -68,39 +57,73 @@ Main folder and its exposed functions
- getCustomerId
- getCustomerWistlist
- `cart`
-
- useCart
- useAddItem
- useRemoveItem
- useUpdateItem
+- `env.template`
+- `provider.ts`
+- `commerce.config.json`
+- `next.config.js`
+- `README.md`
-- `config.json`
-- README.md
+## Configuration
-#### Example of correct usage of Commerce Framework
+### How to change providers
-```js
-import { useUI } from '@components/ui'
-import { useCustomer } from '@framework/customer'
-import { useWishlist, useAddItem, useRemoveItem } from '@framework/wishlist'
-```
+First, update the provider selected in `commerce.config.json`:
-## Config
-
-### Features
-
-In order to make the UI entirely functional, we need to specify which features certain providers do not **provide**.
-
-**Disabling wishlist:**
-
-```
+```json
{
+ "provider": "bigcommerce",
"features": {
- "wishlist": false
+ "wishlist": true
}
}
```
+Then, change the paths defined in `tsconfig.json` and update the `@framework` paths to point to the right folder provider:
+
+```json
+"@framework": ["framework/bigcommerce"],
+"@framework/*": ["framework/bigcommerce/*"]
+```
+
+Make sure to add the environment variables required by the new provider.
+
+### Features
+
+Every provider defines the features that it supports under `framework/{provider}/commerce.config.json`
+
+#### How to turn Features on and off
+
+> NOTE: The selected provider should support the feature that you are toggling. (This means that you can't turn wishlist on if the provider doesn't support this functionality out the box)
+
+- Open `commerce.config.json`
+- You'll see a config file like this:
+ ```json
+ {
+ "provider": "bigcommerce",
+ "features": {
+ "wishlist": false
+ }
+ }
+ ```
+- Turn wishlist on by setting wishlist to true.
+- Run the app and the wishlist functionality should be back on.
+
+### How to create a new provider
+
+We'd recommend to duplicate a provider folder and push your providers SDK.
+
+If you succeeded building a provider, submit a PR so we can all enjoy it.
+
+## Work in progress
+
+We're using Github Projects to keep track of issues in progress and todo's. Here is our [Board](https://github.com/vercel/commerce/projects/1)
+
+People actively working on this project: @okbel & @lfades.
+
## Contribute
Our commitment to Open Source can be found [here](https://vercel.com/oss).
diff --git a/components/product/ProductView/ProductView.module.css b/components/product/ProductView/ProductView.module.css
index dec06f665..6545d611b 100644
--- a/components/product/ProductView/ProductView.module.css
+++ b/components/product/ProductView/ProductView.module.css
@@ -7,7 +7,7 @@
}
.productDisplay {
- @apply relative flex px-0 pb-0 relative box-border col-span-1 bg-violet;
+ @apply relative flex px-0 pb-0 box-border col-span-1 bg-violet;
min-height: 600px;
@screen md {
diff --git a/components/product/Swatch/Swatch.module.css b/components/product/Swatch/Swatch.module.css
index ae37771ad..051435afd 100644
--- a/components/product/Swatch/Swatch.module.css
+++ b/components/product/Swatch/Swatch.module.css
@@ -1,4 +1,5 @@
.root {
+ composes: root from 'components/ui/Button/Button.module.css';
@apply h-12 w-12 bg-primary text-primary rounded-full mr-3 inline-flex
items-center justify-center cursor-pointer transition duration-150 ease-in-out
p-0 shadow-none border-gray-200 border box-border;
diff --git a/components/product/helpers.ts b/components/product/helpers.ts
index 029476c92..44bdeca97 100644
--- a/components/product/helpers.ts
+++ b/components/product/helpers.ts
@@ -8,14 +8,18 @@ export type SelectedOptions = {
export function getVariant(product: Product, opts: SelectedOptions) {
const variant = product.variants.find((variant) => {
return Object.entries(opts).every(([key, value]) =>
- variant.options.find((option) => {
- if (
- option.__typename === 'MultipleChoiceOption' &&
- option.displayName.toLowerCase() === key.toLowerCase()
- ) {
- return option.values.find((v) => v.label.toLowerCase() === value)
- }
- })
+ value
+ ? variant.options.find((option) => {
+ if (
+ option.__typename === 'MultipleChoiceOption' &&
+ option.displayName.toLowerCase() === key.toLowerCase()
+ ) {
+ return option.values.find((v) => v.label.toLowerCase() === value)
+ }
+ })
+ : !variant.options.find(
+ (v) => v.displayName.toLowerCase() === key.toLowerCase()
+ )
)
})
return variant
diff --git a/config/seo.json b/config/seo.json
index 0d0c3d37d..82520cf9b 100644
--- a/config/seo.json
+++ b/config/seo.json
@@ -1,12 +1,22 @@
{
"title": "ACME Storefront | Powered by Next.js Commerce",
"titleTemplate": "%s - ACME Storefront",
- "description": "Next.js Commerce -> https://www.nextjs.org/commerce",
+ "description": "Next.js Commerce - https://www.nextjs.org/commerce",
"openGraph": {
+ "title": "ACME Storefront | Powered by Next.js Commerce",
+ "description": "Next.js Commerce - https://www.nextjs.org/commerce",
"type": "website",
"locale": "en_IE",
"url": "https://nextjs.org/commerce",
- "site_name": "Next.js Commerce"
+ "site_name": "Next.js Commerce",
+ "images": [
+ {
+ "url": "/card.png",
+ "width": 800,
+ "height": 600,
+ "alt": "Next.js Commerce"
+ }
+ ]
},
"twitter": {
"handle": "@nextjs",
diff --git a/framework/commerce/with-config.js b/framework/commerce/with-config.js
index da6705cef..1eb1acc19 100644
--- a/framework/commerce/with-config.js
+++ b/framework/commerce/with-config.js
@@ -7,6 +7,7 @@ const merge = require('deepmerge')
const PROVIDERS = ['bigcommerce', 'shopify']
function getProviderName() {
+ // TODO: OSOT.
return process.env.BIGCOMMERCE_STOREFRONT_API_URL ? 'bigcommerce' : null
}
diff --git a/framework/shopify/.env.template b/framework/shopify/.env.template
index 24521c2a1..9dc3674b6 100644
--- a/framework/shopify/.env.template
+++ b/framework/shopify/.env.template
@@ -1,2 +1,2 @@
-SHOPIFY_STORE_DOMAIN=
-SHOPIFY_STOREFRONT_ACCESS_TOKEN=
+NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN=
+NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN=
diff --git a/framework/shopify/api/operations/get-all-collections.ts b/framework/shopify/api/operations/get-all-collections.ts
deleted file mode 100644
index 9cf216a91..000000000
--- a/framework/shopify/api/operations/get-all-collections.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-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
deleted file mode 100644
index 32acb7c8f..000000000
--- a/framework/shopify/api/operations/get-page.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Page } from '../../schema'
-import { ShopifyConfig, getConfig } from '..'
-
-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/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx
index 188dd54a2..7993822cd 100644
--- a/framework/shopify/auth/use-login.tsx
+++ b/framework/shopify/auth/use-login.tsx
@@ -10,7 +10,7 @@ import {
MutationCheckoutCreateArgs,
} from '../schema'
import useLogin, { UseLogin } from '@commerce/auth/use-login'
-import { setCustomerToken } from '../utils'
+import { setCustomerToken, throwUserErrors } from '../utils'
export default useLogin as UseLogin
@@ -45,13 +45,8 @@ export const handler: MutationHook = {
},
})
- const errors = customerAccessTokenCreate?.customerUserErrors
+ throwUserErrors(customerAccessTokenCreate?.customerUserErrors)
- if (errors && errors.length) {
- throw new ValidationError({
- message: getErrorMessage(errors[0]),
- })
- }
const customerAccessToken = customerAccessTokenCreate?.customerAccessToken
const accessToken = customerAccessToken?.accessToken
diff --git a/framework/shopify/auth/use-signup.tsx b/framework/shopify/auth/use-signup.tsx
index 7f66448d3..9ca5c682f 100644
--- a/framework/shopify/auth/use-signup.tsx
+++ b/framework/shopify/auth/use-signup.tsx
@@ -1,15 +1,16 @@
import { useCallback } from 'react'
import type { MutationHook } from '@commerce/utils/types'
-import { CommerceError } from '@commerce/utils/errors'
+import { CommerceError, ValidationError } from '@commerce/utils/errors'
import useSignup, { UseSignup } from '@commerce/auth/use-signup'
import useCustomer from '../customer/use-customer'
-import { CustomerCreateInput } from '../schema'
-
import {
- customerCreateMutation,
- customerAccessTokenCreateMutation,
-} from '../utils/mutations'
-import handleLogin from '../utils/handle-login'
+ CustomerCreateInput,
+ Mutation,
+ MutationCustomerCreateArgs,
+} from '../schema'
+
+import { customerCreateMutation } from '../utils/mutations'
+import { handleAutomaticLogin, throwUserErrors } from '../utils'
export default useSignup as UseSignup
@@ -33,7 +34,11 @@ export const handler: MutationHook<
'A first name, last name, email and password are required to signup',
})
}
- const data = await fetch({
+
+ const { customerCreate } = await fetch<
+ Mutation,
+ MutationCustomerCreateArgs
+ >({
...options,
variables: {
input: {
@@ -45,19 +50,10 @@ export const handler: MutationHook<
},
})
- try {
- const loginData = await fetch({
- query: customerAccessTokenCreateMutation,
- variables: {
- input: {
- email,
- password,
- },
- },
- })
- handleLogin(loginData)
- } catch (error) {}
- return data
+ throwUserErrors(customerCreate?.customerUserErrors)
+ await handleAutomaticLogin(fetch, { email, password })
+
+ return null
},
useHook: ({ fetch }) => () => {
const { revalidate } = useCustomer()
diff --git a/framework/shopify/cart/index.ts b/framework/shopify/cart/index.ts
index 3d288b1df..f6d36b443 100644
--- a/framework/shopify/cart/index.ts
+++ b/framework/shopify/cart/index.ts
@@ -1,3 +1,4 @@
export { default as useCart } from './use-cart'
export { default as useAddItem } from './use-add-item'
+export { default as useUpdateItem } from './use-update-item'
export { default as useRemoveItem } from './use-remove-item'
diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx
index d0f891148..cce0950e9 100644
--- a/framework/shopify/cart/use-add-item.tsx
+++ b/framework/shopify/cart/use-add-item.tsx
@@ -1,12 +1,15 @@
+import { useCallback } from 'react'
import type { MutationHook } from '@commerce/utils/types'
import { CommerceError } from '@commerce/utils/errors'
import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item'
import useCart from './use-cart'
+import {
+ checkoutLineItemAddMutation,
+ getCheckoutId,
+ checkoutToCart,
+} from '../utils'
import { Cart, CartItemBody } from '../types'
-import { checkoutLineItemAddMutation, getCheckoutId } from '../utils'
-import { checkoutToCart } from './utils'
import { Mutation, MutationCheckoutLineItemsAddArgs } from '../schema'
-import { useCallback } from 'react'
export default useAddItem as UseAddItem
@@ -40,8 +43,7 @@ export const handler: MutationHook = {
},
})
- // TODO: Fix this Cart type here
- return checkoutToCart(checkoutLineItemsAdd) as any
+ return checkoutToCart(checkoutLineItemsAdd)
},
useHook: ({ fetch }) => () => {
const { mutate } = useCart()
diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx
index d154bb837..ea1982c2c 100644
--- a/framework/shopify/cart/use-cart.tsx
+++ b/framework/shopify/cart/use-cart.tsx
@@ -6,7 +6,7 @@ import useCommerceCart, {
import { Cart } from '../types'
import { SWRHook } from '@commerce/utils/types'
-import { checkoutCreate, checkoutToCart } from './utils'
+import { checkoutCreate, checkoutToCart } from '../utils'
import getCheckoutQuery from '../utils/queries/get-checkout-query'
export default useCommerceCart as UseCart
@@ -22,6 +22,7 @@ export const handler: SWRHook<
},
async fetcher({ input: { cartId: checkoutId }, options, fetch }) {
let checkout
+
if (checkoutId) {
const data = await fetch({
...options,
@@ -36,8 +37,7 @@ export const handler: SWRHook<
checkout = await checkoutCreate(fetch)
}
- // TODO: Fix this type
- return checkoutToCart({ checkout } as any)
+ return checkoutToCart({ checkout })
},
useHook: ({ useData }) => (input) => {
const response = useData({
diff --git a/framework/shopify/cart/use-remove-item.tsx b/framework/shopify/cart/use-remove-item.tsx
index e2aef13d8..8db38eac2 100644
--- a/framework/shopify/cart/use-remove-item.tsx
+++ b/framework/shopify/cart/use-remove-item.tsx
@@ -1,23 +1,22 @@
import { useCallback } from 'react'
-
import type {
MutationHookContext,
HookFetcherContext,
} from '@commerce/utils/types'
-
+import { RemoveCartItemBody } from '@commerce/types'
import { ValidationError } from '@commerce/utils/errors'
-
import useRemoveItem, {
RemoveItemInput as RemoveItemInputBase,
UseRemoveItem,
} from '@commerce/cart/use-remove-item'
-
import useCart from './use-cart'
-import { checkoutLineItemRemoveMutation, getCheckoutId } from '../utils'
-import { checkoutToCart } from './utils'
+import {
+ checkoutLineItemRemoveMutation,
+ getCheckoutId,
+ checkoutToCart,
+} from '../utils'
import { Cart, LineItem } from '../types'
import { Mutation, MutationCheckoutLineItemsRemoveArgs } from '../schema'
-import { RemoveCartItemBody } from '@commerce/types'
export type RemoveItemFn = T extends LineItem
? (input?: RemoveItemInput) => Promise
diff --git a/framework/shopify/cart/use-update-item.tsx b/framework/shopify/cart/use-update-item.tsx
index 666ce3d08..49dd6be14 100644
--- a/framework/shopify/cart/use-update-item.tsx
+++ b/framework/shopify/cart/use-update-item.tsx
@@ -13,7 +13,7 @@ import useUpdateItem, {
import useCart from './use-cart'
import { handler as removeItemHandler } from './use-remove-item'
import type { Cart, LineItem, UpdateCartItemBody } from '../types'
-import { checkoutToCart } from './utils'
+import { checkoutToCart } from '../utils'
import { getCheckoutId, checkoutLineItemUpdateMutation } from '../utils'
import { Mutation, MutationCheckoutLineItemsUpdateArgs } from '../schema'
diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts
deleted file mode 100644
index 03005f342..000000000
--- a/framework/shopify/cart/utils/checkout-to-cart.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { Cart } from '../../types'
-import { CommerceError, ValidationError } from '@commerce/utils/errors'
-
-import {
- CheckoutLineItemsAddPayload,
- CheckoutLineItemsRemovePayload,
- CheckoutLineItemsUpdatePayload,
- Maybe,
-} from '../../schema'
-import { normalizeCart } from '../../utils'
-
-export type CheckoutPayload =
- | CheckoutLineItemsAddPayload
- | CheckoutLineItemsUpdatePayload
- | CheckoutLineItemsRemovePayload
-
-const checkoutToCart = (checkoutPayload?: Maybe): Cart => {
- if (!checkoutPayload) {
- throw new CommerceError({
- message: 'Invalid response from Shopify',
- })
- }
-
- const checkout = checkoutPayload?.checkout
- const userErrors = checkoutPayload?.userErrors
-
- if (userErrors && userErrors.length) {
- throw new ValidationError({
- message: userErrors[0].message,
- })
- }
-
- if (!checkout) {
- throw new CommerceError({
- message: 'Invalid response from Shopify',
- })
- }
-
- return normalizeCart(checkout)
-}
-
-export default checkoutToCart
diff --git a/framework/shopify/cart/utils/fetcher.ts b/framework/shopify/cart/utils/fetcher.ts
deleted file mode 100644
index 6afb55f18..000000000
--- a/framework/shopify/cart/utils/fetcher.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { HookFetcherFn } from '@commerce/utils/types'
-import { Cart } from '@commerce/types'
-import { checkoutCreate, checkoutToCart } from '.'
-import { FetchCartInput } from '@commerce/cart/use-cart'
-
-const fetcher: HookFetcherFn = async ({
- options,
- input: { cartId: checkoutId },
- fetch,
-}) => {
- let checkout
-
- if (checkoutId) {
- const data = await fetch({
- ...options,
- variables: {
- checkoutId,
- },
- })
- checkout = data.node
- }
-
- if (checkout?.completedAt || !checkoutId) {
- checkout = await checkoutCreate(fetch)
- }
-
- // TODO: Fix this type
- return checkoutToCart({ checkout } as any)
-}
-
-export default fetcher
diff --git a/framework/shopify/cart/utils/index.ts b/framework/shopify/cart/utils/index.ts
deleted file mode 100644
index 20d04955d..000000000
--- a/framework/shopify/cart/utils/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { default as checkoutToCart } from './checkout-to-cart'
-export { default as checkoutCreate } from './checkout-create'
diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx
index 425df9e83..bf812af3d 100644
--- a/framework/shopify/product/use-search.tsx
+++ b/framework/shopify/product/use-search.tsx
@@ -48,7 +48,8 @@ export const handler: SWRHook<
edges = data.node?.products?.edges ?? []
if (brandId) {
edges = edges.filter(
- ({ node: { vendor } }: ProductEdge) => vendor === brandId
+ ({ node: { vendor } }: ProductEdge) =>
+ vendor.replace(/\s+/g, '-').toLowerCase() === brandId
)
}
} else {
diff --git a/framework/shopify/provider.ts b/framework/shopify/provider.ts
index 383822baa..5041770c6 100644
--- a/framework/shopify/provider.ts
+++ b/framework/shopify/provider.ts
@@ -23,9 +23,6 @@ export const shopifyProvider = {
customer: { useCustomer },
products: { useSearch },
auth: { useLogin, useLogout, useSignup },
- features: {
- wishlist: false,
- },
}
export type ShopifyProvider = typeof shopifyProvider
diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/utils/checkout-create.ts
similarity index 62%
rename from framework/shopify/cart/utils/checkout-create.ts
rename to framework/shopify/utils/checkout-create.ts
index e950cc7e4..359d16315 100644
--- a/framework/shopify/cart/utils/checkout-create.ts
+++ b/framework/shopify/utils/checkout-create.ts
@@ -1,13 +1,17 @@
+import Cookies from 'js-cookie'
+
import {
SHOPIFY_CHECKOUT_ID_COOKIE,
SHOPIFY_CHECKOUT_URL_COOKIE,
SHOPIFY_COOKIE_EXPIRE,
-} from '../../const'
+} from '../const'
-import checkoutCreateMutation from '../../utils/mutations/checkout-create'
-import Cookies from 'js-cookie'
+import checkoutCreateMutation from './mutations/checkout-create'
+import { CheckoutCreatePayload } from '../schema'
-export const checkoutCreate = async (fetch: any) => {
+export const checkoutCreate = async (
+ fetch: any
+): Promise => {
const data = await fetch({
query: checkoutCreateMutation,
})
@@ -20,7 +24,7 @@ export const checkoutCreate = async (fetch: any) => {
expires: SHOPIFY_COOKIE_EXPIRE,
}
Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId, options)
- Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout?.webUrl, options)
+ Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout.webUrl, options)
}
return checkout
diff --git a/framework/shopify/utils/checkout-to-cart.ts b/framework/shopify/utils/checkout-to-cart.ts
new file mode 100644
index 000000000..034ff11d7
--- /dev/null
+++ b/framework/shopify/utils/checkout-to-cart.ts
@@ -0,0 +1,48 @@
+import { Cart } from '../types'
+import { CommerceError } from '@commerce/utils/errors'
+
+import {
+ CheckoutLineItemsAddPayload,
+ CheckoutLineItemsRemovePayload,
+ CheckoutLineItemsUpdatePayload,
+ CheckoutCreatePayload,
+ CheckoutUserError,
+ Checkout,
+ Maybe,
+} from '../schema'
+
+import { normalizeCart } from './normalize'
+import throwUserErrors from './throw-user-errors'
+
+export type CheckoutQuery = {
+ checkout: Checkout
+ checkoutUserErrors?: Array
+}
+
+export type CheckoutPayload =
+ | CheckoutLineItemsAddPayload
+ | CheckoutLineItemsUpdatePayload
+ | CheckoutLineItemsRemovePayload
+ | CheckoutCreatePayload
+ | CheckoutQuery
+
+const checkoutToCart = (checkoutPayload?: Maybe): Cart => {
+ if (!checkoutPayload) {
+ throw new CommerceError({
+ message: 'Missing checkout payload from response',
+ })
+ }
+
+ const checkout = checkoutPayload?.checkout
+ throwUserErrors(checkoutPayload?.checkoutUserErrors)
+
+ if (!checkout) {
+ throw new CommerceError({
+ message: 'Missing checkout object from response',
+ })
+ }
+
+ return normalizeCart(checkout)
+}
+
+export default checkoutToCart
diff --git a/framework/shopify/utils/get-sort-variables.ts b/framework/shopify/utils/get-sort-variables.ts
index b8cdeec51..141d9a180 100644
--- a/framework/shopify/utils/get-sort-variables.ts
+++ b/framework/shopify/utils/get-sort-variables.ts
@@ -1,4 +1,4 @@
-const getSortVariables = (sort?: string, isCategory = false) => {
+const getSortVariables = (sort?: string, isCategory: boolean = false) => {
let output = {}
switch (sort) {
case 'price-asc':
diff --git a/framework/shopify/utils/get-vendors.ts b/framework/shopify/utils/get-vendors.ts
index f04483bb1..24843f177 100644
--- a/framework/shopify/utils/get-vendors.ts
+++ b/framework/shopify/utils/get-vendors.ts
@@ -2,13 +2,14 @@ import { ShopifyConfig } from '../api'
import fetchAllProducts from '../api/utils/fetch-all-products'
import getAllProductVendors from './queries/get-all-product-vendors-query'
-export type BrandNode = {
+export type Brand = {
+ entityId: string
name: string
path: string
}
export type BrandEdge = {
- node: BrandNode
+ node: Brand
}
export type Brands = BrandEdge[]
@@ -24,13 +25,16 @@ const getVendors = async (config: ShopifyConfig): Promise => {
let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor)
- return [...new Set(vendorsStrings)].map((v) => ({
- node: {
- entityId: v,
- name: v,
- path: `brands/${v}`,
- },
- }))
+ return [...new Set(vendorsStrings)].map((v) => {
+ const id = v.replace(/\s+/g, '-').toLowerCase()
+ return {
+ node: {
+ entityId: id,
+ name: v,
+ path: `brands/${id}`,
+ },
+ }
+ })
}
export default getVendors
diff --git a/framework/shopify/utils/handle-account-activation.ts b/framework/shopify/utils/handle-account-activation.ts
new file mode 100644
index 000000000..d11f80ba1
--- /dev/null
+++ b/framework/shopify/utils/handle-account-activation.ts
@@ -0,0 +1,30 @@
+import { FetcherOptions } from '@commerce/utils/types'
+import throwUserErrors from './throw-user-errors'
+
+import {
+ MutationCustomerActivateArgs,
+ MutationCustomerActivateByUrlArgs,
+} from '../schema'
+import { Mutation } from '../schema'
+import { customerActivateByUrlMutation } from './mutations'
+
+const handleAccountActivation = async (
+ fetch: (options: FetcherOptions) => Promise,
+ input: MutationCustomerActivateByUrlArgs
+) => {
+ try {
+ const { customerActivateByUrl } = await fetch<
+ Mutation,
+ MutationCustomerActivateArgs
+ >({
+ query: customerActivateByUrlMutation,
+ variables: {
+ input,
+ },
+ })
+
+ throwUserErrors(customerActivateByUrl?.customerUserErrors)
+ } catch (error) {}
+}
+
+export default handleAccountActivation
diff --git a/framework/shopify/utils/handle-login.ts b/framework/shopify/utils/handle-login.ts
index 77b6873e3..de86fa1d2 100644
--- a/framework/shopify/utils/handle-login.ts
+++ b/framework/shopify/utils/handle-login.ts
@@ -1,30 +1,12 @@
-import { ValidationError } from '@commerce/utils/errors'
+import { FetcherOptions } from '@commerce/utils/types'
+import { CustomerAccessTokenCreateInput } from '../schema'
import { setCustomerToken } from './customer-token'
-
-const getErrorMessage = ({
- code,
- message,
-}: {
- code: string
- message: string
-}) => {
- switch (code) {
- case 'UNIDENTIFIED_CUSTOMER':
- message = 'Cannot find an account that matches the provided credentials'
- break
- }
- return message
-}
+import { customerAccessTokenCreateMutation } from './mutations'
+import throwUserErrors from './throw-user-errors'
const handleLogin = (data: any) => {
const response = data.customerAccessTokenCreate
- const errors = response?.customerUserErrors
-
- if (errors && errors.length) {
- throw new ValidationError({
- message: getErrorMessage(errors[0]),
- })
- }
+ throwUserErrors(response?.customerUserErrors)
const customerAccessToken = response?.customerAccessToken
const accessToken = customerAccessToken?.accessToken
@@ -36,4 +18,19 @@ const handleLogin = (data: any) => {
return customerAccessToken
}
+export const handleAutomaticLogin = async (
+ fetch: (options: FetcherOptions) => Promise,
+ input: CustomerAccessTokenCreateInput
+) => {
+ try {
+ const loginData = await fetch({
+ query: customerAccessTokenCreateMutation,
+ variables: {
+ input,
+ },
+ })
+ handleLogin(loginData)
+ } catch (error) {}
+}
+
export default handleLogin
diff --git a/framework/shopify/utils/index.ts b/framework/shopify/utils/index.ts
index 2d59aa506..61e5975d7 100644
--- a/framework/shopify/utils/index.ts
+++ b/framework/shopify/utils/index.ts
@@ -4,6 +4,11 @@ export { default as getSortVariables } from './get-sort-variables'
export { default as getVendors } from './get-vendors'
export { default as getCategories } from './get-categories'
export { default as getCheckoutId } from './get-checkout-id'
+export { default as checkoutCreate } from './checkout-create'
+export { default as checkoutToCart } from './checkout-to-cart'
+export { default as handleLogin, handleAutomaticLogin } from './handle-login'
+export { default as handleAccountActivation } from './handle-account-activation'
+export { default as throwUserErrors } from './throw-user-errors'
export * from './queries'
export * from './mutations'
export * from './normalize'
diff --git a/framework/shopify/utils/mutations/checkout-create.ts b/framework/shopify/utils/mutations/checkout-create.ts
index 912e1cbd2..ffbd555c7 100644
--- a/framework/shopify/utils/mutations/checkout-create.ts
+++ b/framework/shopify/utils/mutations/checkout-create.ts
@@ -3,9 +3,10 @@ import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutCreateMutation = /* GraphQL */ `
mutation {
checkoutCreate(input: {}) {
- userErrors {
- message
+ checkoutUserErrors {
+ code
field
+ message
}
checkout {
${checkoutDetailsFragment}
diff --git a/framework/shopify/utils/mutations/checkout-line-item-add.ts b/framework/shopify/utils/mutations/checkout-line-item-add.ts
index 67b9cf250..2282c4e26 100644
--- a/framework/shopify/utils/mutations/checkout-line-item-add.ts
+++ b/framework/shopify/utils/mutations/checkout-line-item-add.ts
@@ -3,9 +3,10 @@ import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutLineItemAddMutation = /* GraphQL */ `
mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemInput!]!) {
checkoutLineItemsAdd(checkoutId: $checkoutId, lineItems: $lineItems) {
- userErrors {
- message
+ checkoutUserErrors {
+ code
field
+ message
}
checkout {
${checkoutDetailsFragment}
diff --git a/framework/shopify/utils/mutations/checkout-line-item-remove.ts b/framework/shopify/utils/mutations/checkout-line-item-remove.ts
index d967a5168..8dea4ce08 100644
--- a/framework/shopify/utils/mutations/checkout-line-item-remove.ts
+++ b/framework/shopify/utils/mutations/checkout-line-item-remove.ts
@@ -6,9 +6,10 @@ const checkoutLineItemRemoveMutation = /* GraphQL */ `
checkoutId: $checkoutId
lineItemIds: $lineItemIds
) {
- userErrors {
- message
+ checkoutUserErrors {
+ code
field
+ message
}
checkout {
${checkoutDetailsFragment}
diff --git a/framework/shopify/utils/mutations/checkout-line-item-update.ts b/framework/shopify/utils/mutations/checkout-line-item-update.ts
index 8edf17587..76254341e 100644
--- a/framework/shopify/utils/mutations/checkout-line-item-update.ts
+++ b/framework/shopify/utils/mutations/checkout-line-item-update.ts
@@ -3,9 +3,10 @@ import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutLineItemUpdateMutation = /* GraphQL */ `
mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemUpdateInput!]!) {
checkoutLineItemsUpdate(checkoutId: $checkoutId, lineItems: $lineItems) {
- userErrors {
- message
+ checkoutUserErrors {
+ code
field
+ message
}
checkout {
${checkoutDetailsFragment}
diff --git a/framework/shopify/utils/mutations/customer-access-token-delete.ts b/framework/shopify/utils/mutations/customer-access-token-delete.ts
index c46eff1e5..208e69c09 100644
--- a/framework/shopify/utils/mutations/customer-access-token-delete.ts
+++ b/framework/shopify/utils/mutations/customer-access-token-delete.ts
@@ -3,7 +3,7 @@ const customerAccessTokenDeleteMutation = /* GraphQL */ `
customerAccessTokenDelete(customerAccessToken: $customerAccessToken) {
deletedAccessToken
deletedCustomerAccessTokenId
- userErrors {
+ customerUserErrors {
field
message
}
diff --git a/framework/shopify/utils/mutations/customer-activate-by-url.ts b/framework/shopify/utils/mutations/customer-activate-by-url.ts
new file mode 100644
index 000000000..345d502bd
--- /dev/null
+++ b/framework/shopify/utils/mutations/customer-activate-by-url.ts
@@ -0,0 +1,19 @@
+const customerActivateByUrlMutation = /* GraphQL */ `
+ mutation customerActivateByUrl($activationUrl: URL!, $password: String!) {
+ customerActivateByUrl(activationUrl: $activationUrl, password: $password) {
+ customer {
+ id
+ }
+ customerAccessToken {
+ accessToken
+ expiresAt
+ }
+ customerUserErrors {
+ code
+ field
+ message
+ }
+ }
+ }
+`
+export default customerActivateByUrlMutation
diff --git a/framework/shopify/utils/mutations/customer-activate.ts b/framework/shopify/utils/mutations/customer-activate.ts
new file mode 100644
index 000000000..b1d057c69
--- /dev/null
+++ b/framework/shopify/utils/mutations/customer-activate.ts
@@ -0,0 +1,19 @@
+const customerActivateMutation = /* GraphQL */ `
+ mutation customerActivate($id: ID!, $input: CustomerActivateInput!) {
+ customerActivate(id: $id, input: $input) {
+ customer {
+ id
+ }
+ customerAccessToken {
+ accessToken
+ expiresAt
+ }
+ customerUserErrors {
+ code
+ field
+ message
+ }
+ }
+ }
+`
+export default customerActivateMutation
diff --git a/framework/shopify/utils/mutations/index.ts b/framework/shopify/utils/mutations/index.ts
index 3a16d7cec..165fb192d 100644
--- a/framework/shopify/utils/mutations/index.ts
+++ b/framework/shopify/utils/mutations/index.ts
@@ -5,3 +5,5 @@ export { default as checkoutLineItemUpdateMutation } from './checkout-line-item-
export { default as checkoutLineItemRemoveMutation } from './checkout-line-item-remove'
export { default as customerAccessTokenCreateMutation } from './customer-access-token-create'
export { default as customerAccessTokenDeleteMutation } from './customer-access-token-delete'
+export { default as customerActivateMutation } from './customer-activate'
+export { default as customerActivateByUrlMutation } from './customer-activate-by-url'
diff --git a/framework/shopify/utils/normalize.ts b/framework/shopify/utils/normalize.ts
index c9b428b37..5f11fe7c6 100644
--- a/framework/shopify/utils/normalize.ts
+++ b/framework/shopify/utils/normalize.ts
@@ -33,7 +33,7 @@ const normalizeProductOption = ({
let output: any = {
label: value,
}
- if (displayName === 'Color') {
+ if (displayName.match(/colou?r/gi)) {
output = {
...output,
hexColors: [value],
@@ -54,21 +54,24 @@ const normalizeProductVariants = ({ edges }: ProductVariantConnection) => {
return edges?.map(
({
node: { id, selectedOptions, sku, title, priceV2, compareAtPriceV2 },
- }) => ({
- id,
- name: title,
- sku: sku ?? id,
- price: +priceV2.amount,
- listPrice: +compareAtPriceV2?.amount,
- requiresShipping: true,
- options: selectedOptions.map(({ name, value }: SelectedOption) =>
- normalizeProductOption({
- id,
- name,
- values: [value],
- })
- ),
- })
+ }) => {
+ return {
+ id,
+ name: title,
+ sku: sku ?? id,
+ price: +priceV2.amount,
+ listPrice: +compareAtPriceV2?.amount,
+ requiresShipping: true,
+ options: selectedOptions.map(({ name, value }: SelectedOption) => {
+ const options = normalizeProductOption({
+ id,
+ name,
+ values: [value],
+ })
+ return options
+ }),
+ }
+ }
)
}
@@ -96,7 +99,12 @@ export function normalizeProduct(productNode: ShopifyProduct): Product {
price: money(priceRange?.minVariantPrice),
images: normalizeProductImages(images),
variants: variants ? normalizeProductVariants(variants) : [],
- options: options ? options.map((o) => normalizeProductOption(o)) : [],
+ options:
+ options
+ ?.filter(({ name, values }) => {
+ return name !== 'Title' && values !== ['Default Title']
+ })
+ .map((o) => normalizeProductOption(o)) ?? [],
...rest,
}
@@ -122,7 +130,7 @@ export function normalizeCart(checkout: Checkout): Cart {
}
function normalizeLineItem({
- node: { id, title, variant, quantity },
+ node: { id, title, variant, quantity, ...rest },
}: CheckoutLineItemEdge): LineItem {
return {
id,
@@ -135,7 +143,7 @@ function normalizeLineItem({
sku: variant?.sku ?? '',
name: variant?.title!,
image: {
- url: variant?.image?.originalSrc,
+ url: variant?.image?.originalSrc ?? '/product-img-placeholder.svg',
},
requiresShipping: variant?.requiresShipping ?? false,
price: variant?.priceV2?.amount,
@@ -143,10 +151,13 @@ function normalizeLineItem({
},
path: '',
discounts: [],
- options: [
- {
- value: variant?.title,
- },
- ],
+ options:
+ variant?.title !== 'Default Title'
+ ? [
+ {
+ value: variant?.title,
+ },
+ ]
+ : [],
}
}
diff --git a/framework/shopify/utils/throw-user-errors.ts b/framework/shopify/utils/throw-user-errors.ts
new file mode 100644
index 000000000..5488ba282
--- /dev/null
+++ b/framework/shopify/utils/throw-user-errors.ts
@@ -0,0 +1,38 @@
+import { ValidationError } from '@commerce/utils/errors'
+
+import {
+ CheckoutErrorCode,
+ CheckoutUserError,
+ CustomerErrorCode,
+ CustomerUserError,
+} from '../schema'
+
+export type UserErrors = Array
+
+export type UserErrorCode =
+ | CustomerErrorCode
+ | CheckoutErrorCode
+ | null
+ | undefined
+
+const getCustomMessage = (code: UserErrorCode, message: string) => {
+ switch (code) {
+ case 'UNIDENTIFIED_CUSTOMER':
+ message = 'Cannot find an account that matches the provided credentials'
+ break
+ }
+ return message
+}
+
+export const throwUserErrors = (errors?: UserErrors) => {
+ if (errors && errors.length) {
+ throw new ValidationError({
+ errors: errors.map(({ code, message }) => ({
+ code: code ?? 'validation_error',
+ message: getCustomMessage(code, message),
+ })),
+ })
+ }
+}
+
+export default throwUserErrors
diff --git a/framework/shopify/wishlist/use-add-item.tsx b/framework/shopify/wishlist/use-add-item.tsx
index 75f067c3a..438397f2b 100644
--- a/framework/shopify/wishlist/use-add-item.tsx
+++ b/framework/shopify/wishlist/use-add-item.tsx
@@ -1,13 +1,34 @@
import { useCallback } from 'react'
+import type { MutationHook } from '@commerce/utils/types'
+import { CommerceError } from '@commerce/utils/errors'
+import useAddItem, { UseAddItem } from '@commerce/wishlist/use-add-item'
-export function emptyHook() {
- const useEmptyHook = async (options = {}) => {
- return useCallback(async function () {
- return Promise.resolve()
- }, [])
- }
+import useCustomer from '../customer/use-customer'
+import useWishlist from './use-wishlist'
- return useEmptyHook
+export default useAddItem as UseAddItem
+
+export const handler: MutationHook = {
+ fetchOptions: {
+ query: '',
+ },
+ useHook: ({ fetch }) => () => {
+ const { data: customer } = useCustomer()
+ const { revalidate } = useWishlist()
+
+ return useCallback(
+ async function addItem(item) {
+ if (!customer) {
+ // A signed customer is required in order to have a wishlist
+ throw new CommerceError({
+ message: 'Signed customer not found',
+ })
+ }
+
+ await revalidate()
+ return null
+ },
+ [fetch, revalidate, customer]
+ )
+ },
}
-
-export default emptyHook
diff --git a/framework/shopify/wishlist/use-remove-item.tsx b/framework/shopify/wishlist/use-remove-item.tsx
index a2d3a8a05..971ec082f 100644
--- a/framework/shopify/wishlist/use-remove-item.tsx
+++ b/framework/shopify/wishlist/use-remove-item.tsx
@@ -1,17 +1,36 @@
import { useCallback } from 'react'
+import type { MutationHook } from '@commerce/utils/types'
+import { CommerceError } from '@commerce/utils/errors'
+import useRemoveItem, {
+ UseRemoveItem,
+} from '@commerce/wishlist/use-remove-item'
-type Options = {
- includeProducts?: boolean
+import useCustomer from '../customer/use-customer'
+import useWishlist from './use-wishlist'
+
+export default useRemoveItem as UseRemoveItem
+
+export const handler: MutationHook = {
+ fetchOptions: {
+ query: '',
+ },
+ useHook: ({ fetch }) => () => {
+ const { data: customer } = useCustomer()
+ const { revalidate } = useWishlist()
+
+ return useCallback(
+ async function addItem(item) {
+ if (!customer) {
+ // A signed customer is required in order to have a wishlist
+ throw new CommerceError({
+ message: 'Signed customer not found',
+ })
+ }
+
+ await revalidate()
+ return null
+ },
+ [fetch, revalidate, customer]
+ )
+ },
}
-
-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
index d2ce9db5b..651ea06c8 100644
--- a/framework/shopify/wishlist/use-wishlist.tsx
+++ b/framework/shopify/wishlist/use-wishlist.tsx
@@ -1,46 +1,49 @@
-// TODO: replace this hook and other wishlist hooks with a handler, or remove them if
-// Shopify doesn't have a wishlist
+import { useMemo } from 'react'
+import { SWRHook } from '@commerce/utils/types'
+import useWishlist, { UseWishlist } from '@commerce/wishlist/use-wishlist'
+import useCustomer from '../customer/use-customer'
-import { HookFetcher } from '@commerce/utils/types'
-import { Product } from '../schema'
+export type UseWishlistInput = { includeProducts?: boolean }
-const defaultOpts = {}
+export default useWishlist as UseWishlist
-export type Wishlist = {
- items: [
- {
- product_id: number
- variant_id: number
- id: number
- product: Product
- }
- ]
+export const handler: SWRHook<
+ any | null,
+ UseWishlistInput,
+ { customerId?: number } & UseWishlistInput,
+ { isEmpty?: boolean }
+> = {
+ fetchOptions: {
+ url: '/api/bigcommerce/wishlist',
+ method: 'GET',
+ },
+ fetcher() {
+ return { items: [] }
+ },
+ useHook: ({ useData }) => (input) => {
+ const { data: customer } = useCustomer()
+ const response = useData({
+ input: [
+ ['customerId', customer?.entityId],
+ ['includeProducts', input?.includeProducts],
+ ],
+ swrOptions: {
+ revalidateOnFocus: false,
+ ...input?.swrOptions,
+ },
+ })
+
+ return useMemo(
+ () =>
+ Object.create(response, {
+ isEmpty: {
+ get() {
+ return (response.data?.items?.length || 0) <= 0
+ },
+ enumerable: true,
+ },
+ }),
+ [response]
+ )
+ },
}
-
-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
- swrOptions?: any
-) {
- const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => {
- return { data: null }
- }
-
- useWishlist.extend = extendHook
-
- return useWishlist
-}
-
-export default extendHook(fetcher)
diff --git a/lib/colors.ts b/lib/colors.ts
index 355201146..139cda23d 100644
--- a/lib/colors.ts
+++ b/lib/colors.ts
@@ -42,9 +42,161 @@ function hexToRgb(hex: string = '') {
return [r, g, b]
}
-export function isDark(color = '') {
+const colorMap: Record = {
+ aliceblue: '#F0F8FF',
+ antiquewhite: '#FAEBD7',
+ aqua: '#00FFFF',
+ aquamarine: '#7FFFD4',
+ azure: '#F0FFFF',
+ beige: '#F5F5DC',
+ bisque: '#FFE4C4',
+ black: '#000000',
+ blanchedalmond: '#FFEBCD',
+ blue: '#0000FF',
+ blueviolet: '#8A2BE2',
+ brown: '#A52A2A',
+ burlywood: '#DEB887',
+ cadetblue: '#5F9EA0',
+ chartreuse: '#7FFF00',
+ chocolate: '#D2691E',
+ coral: '#FF7F50',
+ cornflowerblue: '#6495ED',
+ cornsilk: '#FFF8DC',
+ crimson: '#DC143C',
+ cyan: '#00FFFF',
+ darkblue: '#00008B',
+ darkcyan: '#008B8B',
+ darkgoldenrod: '#B8860B',
+ darkgray: '#A9A9A9',
+ darkgreen: '#006400',
+ darkgrey: '#A9A9A9',
+ darkkhaki: '#BDB76B',
+ darkmagenta: '#8B008B',
+ darkolivegreen: '#556B2F',
+ darkorange: '#FF8C00',
+ darkorchid: '#9932CC',
+ darkred: '#8B0000',
+ darksalmon: '#E9967A',
+ darkseagreen: '#8FBC8F',
+ darkslateblue: '#483D8B',
+ darkslategray: '#2F4F4F',
+ darkslategrey: '#2F4F4F',
+ darkturquoise: '#00CED1',
+ darkviolet: '#9400D3',
+ deeppink: '#FF1493',
+ deepskyblue: '#00BFFF',
+ dimgray: '#696969',
+ dimgrey: '#696969',
+ dodgerblue: '#1E90FF',
+ firebrick: '#B22222',
+ floralwhite: '#FFFAF0',
+ forestgreen: '#228B22',
+ fuchsia: '#FF00FF',
+ gainsboro: '#DCDCDC',
+ ghostwhite: '#F8F8FF',
+ gold: '#FFD700',
+ goldenrod: '#DAA520',
+ gray: '#808080',
+ green: '#008000',
+ greenyellow: '#ADFF2F',
+ grey: '#808080',
+ honeydew: '#F0FFF0',
+ hotpink: '#FF69B4',
+ indianred: '#CD5C5C',
+ indigo: '#4B0082',
+ ivory: '#FFFFF0',
+ khaki: '#F0E68C',
+ lavender: '#E6E6FA',
+ lavenderblush: '#FFF0F5',
+ lawngreen: '#7CFC00',
+ lemonchiffon: '#FFFACD',
+ lightblue: '#ADD8E6',
+ lightcoral: '#F08080',
+ lightcyan: '#E0FFFF',
+ lightgoldenrodyellow: '#FAFAD2',
+ lightgray: '#D3D3D3',
+ lightgreen: '#90EE90',
+ lightgrey: '#D3D3D3',
+ lightpink: '#FFB6C1',
+ lightsalmon: '#FFA07A',
+ lightseagreen: '#20B2AA',
+ lightskyblue: '#87CEFA',
+ lightslategray: '#778899',
+ lightslategrey: '#778899',
+ lightsteelblue: '#B0C4DE',
+ lightyellow: '#FFFFE0',
+ lime: '#00FF00',
+ limegreen: '#32CD32',
+ linen: '#FAF0E6',
+ magenta: '#FF00FF',
+ maroon: '#800000',
+ mediumaquamarine: '#66CDAA',
+ mediumblue: '#0000CD',
+ mediumorchid: '#BA55D3',
+ mediumpurple: '#9370DB',
+ mediumseagreen: '#3CB371',
+ mediumslateblue: '#7B68EE',
+ mediumspringgreen: '#00FA9A',
+ mediumturquoise: '#48D1CC',
+ mediumvioletred: '#C71585',
+ midnightblue: '#191970',
+ mintcream: '#F5FFFA',
+ mistyrose: '#FFE4E1',
+ moccasin: '#FFE4B5',
+ navajowhite: '#FFDEAD',
+ navy: '#000080',
+ oldlace: '#FDF5E6',
+ olive: '#808000',
+ olivedrab: '#6B8E23',
+ orange: '#FFA500',
+ orangered: '#FF4500',
+ orchid: '#DA70D6',
+ palegoldenrod: '#EEE8AA',
+ palegreen: '#98FB98',
+ paleturquoise: '#AFEEEE',
+ palevioletred: '#DB7093',
+ papayawhip: '#FFEFD5',
+ peachpuff: '#FFDAB9',
+ peru: '#CD853F',
+ pink: '#FFC0CB',
+ plum: '#DDA0DD',
+ powderblue: '#B0E0E6',
+ purple: '#800080',
+ rebeccapurple: '#663399',
+ red: '#FF0000',
+ rosybrown: '#BC8F8F',
+ royalblue: '#4169E1',
+ saddlebrown: '#8B4513',
+ salmon: '#FA8072',
+ sandybrown: '#F4A460',
+ seagreen: '#2E8B57',
+ seashell: '#FFF5EE',
+ sienna: '#A0522D',
+ silver: '#C0C0C0',
+ skyblue: '#87CEEB',
+ slateblue: '#6A5ACD',
+ slategray: '#708090',
+ slategrey: '#708090',
+ snow: '#FFFAFA',
+ springgreen: '#00FF7F',
+ steelblue: '#4682B4',
+ tan: '#D2B48C',
+ teal: '#008080',
+ thistle: '#D8BFD8',
+ tomato: '#FF6347',
+ turquoise: '#40E0D0',
+ violet: '#EE82EE',
+ wheat: '#F5DEB3',
+ white: '#FFFFFF',
+ whitesmoke: '#F5F5F5',
+ yellow: '#FFFF00',
+ yellowgreen: '#9ACD32',
+}
+
+export function isDark(color: string = ''): boolean {
+ color = color.toLowerCase()
// Equation from http://24ways.org/2010/calculating-color-contrast
- const rgb = hexToRgb(color)
+ let rgb = colorMap[color] ? hexToRgb(colorMap[color]) : hexToRgb(color)
const res = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000
return res < 128
}
diff --git a/package.json b/package.json
index 906d950dc..4e8fa8d30 100644
--- a/package.json
+++ b/package.json
@@ -14,15 +14,12 @@
"sideEffects": false,
"license": "MIT",
"engines": {
- "node": "12.x"
- },
- "prettier": {
- "semi": false,
- "singleQuote": true
+ "node": "14.x"
},
"dependencies": {
"@reach/portal": "^0.11.2",
"@vercel/fetch": "^6.1.0",
+ "autoprefixer": "^10.2.4",
"body-scroll-lock": "^3.1.5",
"bowser": "^2.11.0",
"classnames": "^2.2.6",
@@ -38,7 +35,7 @@
"next": "^10.0.7",
"next-seo": "^4.11.0",
"next-themes": "^0.0.4",
- "postcss": "^8.2.4",
+ "postcss": "^8.2.6",
"postcss-nesting": "^7.0.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
@@ -47,7 +44,7 @@
"shopify-buy": "^2.11.0",
"swr": "^0.4.0",
"tabbable": "^5.1.5",
- "tailwindcss": "^2.0.2"
+ "tailwindcss": "^2.0.3"
},
"devDependencies": {
"@graphql-codegen/cli": "^1.20.0",
@@ -73,7 +70,7 @@
"next-unused": "^0.0.3",
"postcss-flexbugs-fixes": "^4.2.1",
"postcss-preset-env": "^6.7.0",
- "prettier": "^2.1.2",
+ "prettier": "^2.2.1",
"typescript": "^4.0.3"
},
"resolutions": {
diff --git a/pages/customer/activate.tsx b/pages/customer/activate.tsx
new file mode 100644
index 000000000..61e4da0ca
--- /dev/null
+++ b/pages/customer/activate.tsx
@@ -0,0 +1,26 @@
+import type { GetStaticPropsContext } from 'next'
+import { getConfig } from '@framework/api'
+import getAllPages from '@framework/common/get-all-pages'
+import { Layout } from '@components/common'
+import { Container, Text } from '@components/ui'
+
+export async function getStaticProps({
+ preview,
+ locale,
+}: GetStaticPropsContext) {
+ const config = getConfig({ locale })
+ const { pages } = await getAllPages({ config, preview })
+ return {
+ props: { pages },
+ }
+}
+
+export default function ActivateAccount() {
+ return (
+
+ Activate Your Account
+
+ )
+}
+
+ActivateAccount.Layout = Layout
diff --git a/pages/search.tsx b/pages/search.tsx
index da2edccd8..4100108bc 100644
--- a/pages/search.tsx
+++ b/pages/search.tsx
@@ -75,10 +75,8 @@ export default function Search({
const { data } = useSearch({
search: typeof q === 'string' ? q : '',
- // TODO: Shopify - Fix this type
- categoryId: activeCategory?.entityId as any,
- // TODO: Shopify - Fix this type
- brandId: (activeBrand as any)?.entityId,
+ categoryId: activeCategory?.entityId,
+ brandId: activeBrand?.entityId,
sort: typeof sort === 'string' ? sort : '',
})
diff --git a/public/card.png b/public/card.png
new file mode 100644
index 000000000..40fcf42d1
Binary files /dev/null and b/public/card.png differ
diff --git a/yarn.lock b/yarn.lock
index 9238b1f03..7a1dce814 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1609,6 +1609,18 @@ auto-bind@~4.0.0:
resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb"
integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==
+autoprefixer@^10.2.4:
+ version "10.2.4"
+ resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.2.4.tgz#c0e7cf24fcc6a1ae5d6250c623f0cb8beef2f7e1"
+ integrity sha512-DCCdUQiMD+P/as8m3XkeTUkUKuuRqLGcwD0nll7wevhqoJfMRpJlkFd1+MQh1pvupjiQuip42lc/VFvfUTMSKw==
+ dependencies:
+ browserslist "^4.16.1"
+ caniuse-lite "^1.0.30001181"
+ colorette "^1.2.1"
+ fraction.js "^4.0.13"
+ normalize-range "^0.1.2"
+ postcss-value-parser "^4.1.0"
+
autoprefixer@^9.6.1:
version "9.8.6"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f"
@@ -1818,7 +1830,7 @@ browserslist@4.16.1:
escalade "^3.1.1"
node-releases "^1.1.69"
-browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.6.4:
+browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.1, browserslist@^4.6.4:
version "4.16.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717"
integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==
@@ -3187,6 +3199,11 @@ form-data@^3.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
+fraction.js@^4.0.13:
+ version "4.0.13"
+ resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.0.13.tgz#3c1c315fa16b35c85fffa95725a36fa729c69dfe"
+ integrity sha512-E1fz2Xs9ltlUp+qbiyx9wmt2n9dRzPsS11Jtdb8D2o+cC7wr9xkkKsVKJuBX0ST+LVS+LhLO+SbLJNtfWcJvXA==
+
fs-capacitor@^6.1.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-6.2.0.tgz#fa79ac6576629163cb84561995602d8999afb7f5"
@@ -5586,7 +5603,7 @@ postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.26, postcss@^7.0.
source-map "^0.6.1"
supports-color "^6.1.0"
-postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.4:
+postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.6:
version "8.2.6"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.6.tgz#5d69a974543b45f87e464bc4c3e392a97d6be9fe"
integrity sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg==
@@ -5645,7 +5662,7 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
-prettier@^2.0.5, prettier@^2.1.2:
+prettier@^2.0.5, prettier@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5"
integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==
@@ -6617,7 +6634,7 @@ tabbable@^5.1.5:
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.1.5.tgz#efec48ede268d511c261e3b81facbb4782a35147"
integrity sha512-oVAPrWgLLqrbvQE8XqcU7CVBq6SQbaIbHkhOca3u7/jzuQvyZycrUKPCGr04qpEIUslmUlULbSeN+m3QrKEykA==
-tailwindcss@^2.0.2:
+tailwindcss@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.0.3.tgz#f8d07797d1f89dc4b171673c26237b58783c2c86"
integrity sha512-s8NEqdLBiVbbdL0a5XwTb8jKmIonOuI4RMENEcKLR61jw6SdKvBss7NWZzwCaD+ZIjlgmesv8tmrjXEp7C0eAQ==