From e2368efa9598a7fb68b55b34c8d738e94a2ca236 Mon Sep 17 00:00:00 2001
From: cond0r <pinte_catalin@yahoo.com>
Date: Wed, 3 Mar 2021 17:48:11 +0200
Subject: [PATCH] Fix add to cart & prepare for user activation

---
 components/product/helpers.ts                 |  4 +
 .../api/operations/get-all-collections.ts     | 21 -----
 framework/shopify/api/operations/get-page.ts  | 25 ------
 framework/shopify/auth/use-signup.tsx         | 44 +++++-----
 framework/shopify/cart/use-add-item.tsx       |  3 +-
 framework/shopify/cart/use-cart.tsx           |  4 +-
 .../shopify/cart/utils/checkout-create.ts     |  5 +-
 .../shopify/cart/utils/checkout-to-cart.ts    | 12 ++-
 framework/shopify/product/use-search.tsx      |  3 +-
 framework/shopify/utils/get-vendors.ts        | 22 +++--
 .../utils/handle-account-activation.ts        | 35 ++++++++
 framework/shopify/utils/handle-login.ts       | 18 ++++
 framework/shopify/utils/index.ts              |  2 +
 .../mutations/customer-activate-by-url.ts     | 19 ++++
 .../utils/mutations/customer-activate.ts      | 19 ++++
 framework/shopify/utils/mutations/index.ts    |  2 +
 framework/shopify/utils/normalize.ts          | 59 ++++++++-----
 framework/shopify/wishlist/use-add-item.tsx   | 39 +++++++--
 .../shopify/wishlist/use-remove-item.tsx      | 47 +++++++---
 framework/shopify/wishlist/use-wishlist.tsx   | 87 ++++++++++---------
 pages/customer/activate.tsx                   | 26 ++++++
 pages/search.tsx                              |  6 +-
 tsconfig.json                                 |  4 +-
 23 files changed, 329 insertions(+), 177 deletions(-)
 delete mode 100644 framework/shopify/api/operations/get-all-collections.ts
 delete mode 100644 framework/shopify/api/operations/get-page.ts
 create mode 100644 framework/shopify/utils/handle-account-activation.ts
 create mode 100644 framework/shopify/utils/mutations/customer-activate-by-url.ts
 create mode 100644 framework/shopify/utils/mutations/customer-activate.ts
 create mode 100644 pages/customer/activate.tsx

diff --git a/components/product/helpers.ts b/components/product/helpers.ts
index 029476c92..381dcbc1d 100644
--- a/components/product/helpers.ts
+++ b/components/product/helpers.ts
@@ -14,6 +14,10 @@ export function getVariant(product: Product, opts: SelectedOptions) {
           option.displayName.toLowerCase() === key.toLowerCase()
         ) {
           return option.values.find((v) => v.label.toLowerCase() === value)
+        } else if (!value) {
+          return !variant.options.filter(
+            ({ displayName }) => displayName.toLowerCase() === key
+          ).length
         }
       })
     )
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 extends { page?: any } = { page?: Page }> = T
-
-export type PageVariables = {
-  id: string
-}
-
-async function getPage({
-  url,
-  variables,
-  config,
-  preview,
-}: {
-  url?: string
-  variables: PageVariables
-  config?: ShopifyConfig
-  preview?: boolean
-}): Promise<GetPageResult> {
-  config = getConfig(config)
-  return {}
-}
-
-export default getPage
diff --git a/framework/shopify/auth/use-signup.tsx b/framework/shopify/auth/use-signup.tsx
index 7f66448d3..65ed9f71d 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, handleAccountActivation } from '../utils'
 
 export default useSignup as UseSignup<typeof handler>
 
@@ -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,18 @@ export const handler: MutationHook<
       },
     })
 
-    try {
-      const loginData = await fetch({
-        query: customerAccessTokenCreateMutation,
-        variables: {
-          input: {
-            email,
-            password,
-          },
-        },
+    const errors = customerCreate?.customerUserErrors
+
+    if (errors && errors.length) {
+      const [error] = errors
+      throw new ValidationError({
+        message: error.message,
       })
-      handleLogin(loginData)
-    } catch (error) {}
-    return data
+    }
+
+    await handleAutomaticLogin(fetch, { email, password })
+
+    return null
   },
   useHook: ({ fetch }) => () => {
     const { revalidate } = useCustomer()
diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx
index d0f891148..36f02847b 100644
--- a/framework/shopify/cart/use-add-item.tsx
+++ b/framework/shopify/cart/use-add-item.tsx
@@ -40,8 +40,7 @@ export const handler: MutationHook<Cart, {}, CartItemBody> = {
       },
     })
 
-    // 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..a8be04969 100644
--- a/framework/shopify/cart/use-cart.tsx
+++ b/framework/shopify/cart/use-cart.tsx
@@ -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/utils/checkout-create.ts b/framework/shopify/cart/utils/checkout-create.ts
index e950cc7e4..20c23bcd3 100644
--- a/framework/shopify/cart/utils/checkout-create.ts
+++ b/framework/shopify/cart/utils/checkout-create.ts
@@ -6,8 +6,11 @@ import {
 
 import checkoutCreateMutation from '../../utils/mutations/checkout-create'
 import Cookies from 'js-cookie'
+import { CheckoutCreatePayload } from '../../schema'
 
-export const checkoutCreate = async (fetch: any) => {
+export const checkoutCreate = async (
+  fetch: any
+): Promise<CheckoutCreatePayload> => {
   const data = await fetch({
     query: checkoutCreateMutation,
   })
diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts
index 03005f342..5465e511c 100644
--- a/framework/shopify/cart/utils/checkout-to-cart.ts
+++ b/framework/shopify/cart/utils/checkout-to-cart.ts
@@ -5,14 +5,24 @@ import {
   CheckoutLineItemsAddPayload,
   CheckoutLineItemsRemovePayload,
   CheckoutLineItemsUpdatePayload,
-  Maybe,
+  CheckoutCreatePayload,
+  Checkout,
+  UserError,
 } from '../../schema'
 import { normalizeCart } from '../../utils'
+import { Maybe } from 'framework/bigcommerce/schema'
+
+export type CheckoutQuery = {
+  checkout: Checkout
+  userErrors?: Array<UserError>
+}
 
 export type CheckoutPayload =
   | CheckoutLineItemsAddPayload
   | CheckoutLineItemsUpdatePayload
   | CheckoutLineItemsRemovePayload
+  | CheckoutCreatePayload
+  | CheckoutQuery
 
 const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => {
   if (!checkoutPayload) {
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/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<BrandEdge[]> => {
 
   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..3f5aff5b8
--- /dev/null
+++ b/framework/shopify/utils/handle-account-activation.ts
@@ -0,0 +1,35 @@
+import { ValidationError } from '@commerce/utils/errors'
+import { FetcherOptions } from '@commerce/utils/types'
+import {
+  MutationCustomerActivateArgs,
+  MutationCustomerActivateByUrlArgs,
+} from '../schema'
+import { Mutation } from '../schema'
+import { customerActivateByUrlMutation } from './mutations'
+
+const handleAccountActivation = async (
+  fetch: <T = any, B = Body>(options: FetcherOptions<B>) => Promise<T>,
+  input: MutationCustomerActivateByUrlArgs
+) => {
+  try {
+    const { customerActivateByUrl } = await fetch<
+      Mutation,
+      MutationCustomerActivateArgs
+    >({
+      query: customerActivateByUrlMutation,
+      variables: {
+        input,
+      },
+    })
+
+    const errors = customerActivateByUrl?.customerUserErrors
+    if (errors && errors.length) {
+      const [error] = errors
+      throw new ValidationError({
+        message: error.message,
+      })
+    }
+  } catch (error) {}
+}
+
+export default handleAccountActivation
diff --git a/framework/shopify/utils/handle-login.ts b/framework/shopify/utils/handle-login.ts
index 77b6873e3..0a0a2e8ec 100644
--- a/framework/shopify/utils/handle-login.ts
+++ b/framework/shopify/utils/handle-login.ts
@@ -1,5 +1,8 @@
 import { ValidationError } from '@commerce/utils/errors'
+import { FetcherOptions } from '@commerce/utils/types'
+import { CustomerAccessTokenCreateInput } from '../schema'
 import { setCustomerToken } from './customer-token'
+import { customerAccessTokenCreateMutation } from './mutations'
 
 const getErrorMessage = ({
   code,
@@ -36,4 +39,19 @@ const handleLogin = (data: any) => {
   return customerAccessToken
 }
 
+export const handleAutomaticLogin = async (
+  fetch: <T = any, B = Body>(options: FetcherOptions<B>) => Promise<T>,
+  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..01a5743e5 100644
--- a/framework/shopify/utils/index.ts
+++ b/framework/shopify/utils/index.ts
@@ -4,6 +4,8 @@ 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 handleLogin, handleAutomaticLogin } from './handle-login'
+export { default as handleAccountActivation } from './handle-account-activation'
 export * from './queries'
 export * from './mutations'
 export * from './normalize'
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/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<typeof handler>
+
+export const handler: MutationHook<any, {}, any, any> = {
+  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<typeof handler>
+
+export const handler: MutationHook<any, {}, any, any> = {
+  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<typeof handler>
 
-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<Wishlist | null, UseWishlistInput> = () => {
-  return null
-}
-
-export function extendHook(
-  customFetcher: typeof fetcher,
-  // swrOptions?: SwrOptions<Wishlist | null, UseWishlistInput>
-  swrOptions?: any
-) {
-  const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => {
-    return { data: null }
-  }
-
-  useWishlist.extend = extendHook
-
-  return useWishlist
-}
-
-export default extendHook(fetcher)
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 (
+    <Container>
+      <Text variant="pageHeading">Activate Your Account</Text>
+    </Container>
+  )
+}
+
+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/tsconfig.json b/tsconfig.json
index e20f37099..9e712fb18 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -22,8 +22,8 @@
       "@components/*": ["components/*"],
       "@commerce": ["framework/commerce"],
       "@commerce/*": ["framework/commerce/*"],
-      "@framework": ["framework/shopify"],
-      "@framework/*": ["framework/shopify/*"]
+      "@framework": ["framework/bigcommerce"],
+      "@framework/*": ["framework/bigcommerce/*"]
     }
   },
   "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],