Added actions and more stuff to the wishlist page
This commit is contained in:
		@@ -14,7 +14,7 @@ import type { ProductNode } from '@lib/bigcommerce/api/operations/get-product'
 | 
			
		||||
import {
 | 
			
		||||
  getCurrentVariant,
 | 
			
		||||
  getProductOptions,
 | 
			
		||||
  ProductOptions,
 | 
			
		||||
  SelectedOptions,
 | 
			
		||||
} from '../helpers'
 | 
			
		||||
import WishlistButton from '@components/wishlist/WishlistButton'
 | 
			
		||||
 | 
			
		||||
@@ -29,7 +29,7 @@ const ProductView: FC<Props> = ({ product }) => {
 | 
			
		||||
  const { openSidebar } = useUI()
 | 
			
		||||
  const options = getProductOptions(product)
 | 
			
		||||
  const [loading, setLoading] = useState(false)
 | 
			
		||||
  const [choices, setChoices] = useState<ProductOptions>({
 | 
			
		||||
  const [choices, setChoices] = useState<SelectedOptions>({
 | 
			
		||||
    size: null,
 | 
			
		||||
    color: null,
 | 
			
		||||
  })
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,14 @@
 | 
			
		||||
import { FC } from 'react'
 | 
			
		||||
import { FC, useState } from 'react'
 | 
			
		||||
import cn from 'classnames'
 | 
			
		||||
import Link from 'next/link'
 | 
			
		||||
import Image from 'next/image'
 | 
			
		||||
import type { WishlistItem } from '@lib/bigcommerce/api/wishlist'
 | 
			
		||||
import usePrice from '@lib/commerce/use-price'
 | 
			
		||||
import useRemoveItem from '@lib/bigcommerce/wishlist/use-remove-item'
 | 
			
		||||
import useAddItem from '@lib/bigcommerce/cart/use-add-item'
 | 
			
		||||
import { useUI } from '@components/ui/context'
 | 
			
		||||
import { Button } from '@components/ui'
 | 
			
		||||
import { HTMLContent } from '@components/core'
 | 
			
		||||
import { Trash } from '@components/icons'
 | 
			
		||||
import s from './WishlistCard.module.css'
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +16,7 @@ interface Props {
 | 
			
		||||
  className?: string
 | 
			
		||||
  children?: any
 | 
			
		||||
  data?: ProductData
 | 
			
		||||
  item: WishlistItem
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface ProductData {
 | 
			
		||||
@@ -15,23 +26,83 @@ interface ProductData {
 | 
			
		||||
  path: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const WishlistCard: FC<Props> = ({ className, data }) => {
 | 
			
		||||
const WishlistCard: FC<Props> = ({ className, item }) => {
 | 
			
		||||
  const product = item.product!
 | 
			
		||||
  const { price } = usePrice({
 | 
			
		||||
    amount: product.prices?.price?.value,
 | 
			
		||||
    baseAmount: product.prices?.retailPrice?.value,
 | 
			
		||||
    currencyCode: product.prices?.price?.currencyCode!,
 | 
			
		||||
  })
 | 
			
		||||
  const removeItem = useRemoveItem({ includeProducts: true })
 | 
			
		||||
  const [loading, setLoading] = useState(false)
 | 
			
		||||
  const [removing, setRemoving] = useState(false)
 | 
			
		||||
  const addItem = useAddItem()
 | 
			
		||||
  const { openSidebar } = useUI()
 | 
			
		||||
 | 
			
		||||
  const handleRemove = async () => {
 | 
			
		||||
    setRemoving(true)
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      // If this action succeeds then there's no need to do `setRemoving(true)`
 | 
			
		||||
      // because the component will be removed from the view
 | 
			
		||||
      await removeItem({ id: item.id! })
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      setRemoving(false)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  const addToCart = async () => {
 | 
			
		||||
    setLoading(true)
 | 
			
		||||
    try {
 | 
			
		||||
      await addItem({
 | 
			
		||||
        productId: product.entityId,
 | 
			
		||||
        variantId: product.variants.edges?.[0]?.node.entityId!,
 | 
			
		||||
      })
 | 
			
		||||
      openSidebar()
 | 
			
		||||
      setLoading(false)
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      setLoading(false)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={s.root}>
 | 
			
		||||
      <div className={`col-span-3 ${s.productBg}`} />
 | 
			
		||||
    <div className={cn(s.root, { 'opacity-75 pointer-events-none': removing })}>
 | 
			
		||||
      <div className={`col-span-3 ${s.productBg}`}>
 | 
			
		||||
        <Image
 | 
			
		||||
          src={product.images.edges?.[0]?.node.urlOriginal!}
 | 
			
		||||
          width={400}
 | 
			
		||||
          height={400}
 | 
			
		||||
          alt={product.images.edges?.[0]?.node.altText || 'Product Image'}
 | 
			
		||||
          // The cart item image is already optimized and very small in size
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div className="col-span-7">
 | 
			
		||||
        <h3 className="text-2xl mb-2">Jacket</h3>
 | 
			
		||||
        <p className="mb-4">
 | 
			
		||||
          Biscuit oat cake wafer icing ice cream tiramisu pudding cupcake.
 | 
			
		||||
        </p>
 | 
			
		||||
        <button className="py-1 px-3 border border-secondary rounded-md shadow-sm hover:bg-primary-hover">
 | 
			
		||||
          Add to cart
 | 
			
		||||
        </button>
 | 
			
		||||
        <h3 className="text-2xl mb-2">
 | 
			
		||||
          <Link href={`/product${product.path}`}>
 | 
			
		||||
            <a>{product.name}</a>
 | 
			
		||||
          </Link>
 | 
			
		||||
        </h3>
 | 
			
		||||
        <div className="mb-4">
 | 
			
		||||
          <HTMLContent html={product.description!} />
 | 
			
		||||
        </div>
 | 
			
		||||
        <Button
 | 
			
		||||
          aria-label="Add to Cart"
 | 
			
		||||
          type="button"
 | 
			
		||||
          className={
 | 
			
		||||
            'py-1 px-3 border border-secondary rounded-md shadow-sm hover:bg-primary-hover'
 | 
			
		||||
          }
 | 
			
		||||
          onClick={addToCart}
 | 
			
		||||
          loading={loading}
 | 
			
		||||
        >
 | 
			
		||||
          Add to Cart
 | 
			
		||||
        </Button>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="col-span-2 flex flex-col justify-between">
 | 
			
		||||
        <div className="flex justify-end font-bold">$ 50.00</div>
 | 
			
		||||
        <div className="flex justify-end font-bold">{price}</div>
 | 
			
		||||
        <div className="flex justify-end">
 | 
			
		||||
          <Trash />
 | 
			
		||||
          <button onClick={handleRemove}>
 | 
			
		||||
            <Trash />
 | 
			
		||||
          </button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,13 @@ import { BigcommerceConfig, getConfig } from '..'
 | 
			
		||||
import getAllProducts, { ProductEdge } from './get-all-products'
 | 
			
		||||
 | 
			
		||||
export type Wishlist = Omit<definitions['wishlist_Full'], 'items'> & {
 | 
			
		||||
  items?: (NonNullable<definitions['wishlist_Full']['items']>[0] & {
 | 
			
		||||
    product?: ProductEdge['node']
 | 
			
		||||
  })[]
 | 
			
		||||
  items?: WishlistItem[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type WishlistItem = NonNullable<
 | 
			
		||||
  definitions['wishlist_Full']['items']
 | 
			
		||||
>[0] & {
 | 
			
		||||
  product?: ProductEdge['node']
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type GetCustomerWishlistResult<
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,15 @@ import createApiHandler, {
 | 
			
		||||
  BigcommerceHandler,
 | 
			
		||||
} from '../utils/create-api-handler'
 | 
			
		||||
import { BigcommerceApiError } from '../utils/errors'
 | 
			
		||||
import type { Wishlist } from '../operations/get-customer-wishlist'
 | 
			
		||||
import type {
 | 
			
		||||
  Wishlist,
 | 
			
		||||
  WishlistItem,
 | 
			
		||||
} from '../operations/get-customer-wishlist'
 | 
			
		||||
import getWishlist from './handlers/get-wishlist'
 | 
			
		||||
import addItem from './handlers/add-item'
 | 
			
		||||
import removeItem from './handlers/remove-item'
 | 
			
		||||
 | 
			
		||||
export type { Wishlist }
 | 
			
		||||
export type { Wishlist, WishlistItem }
 | 
			
		||||
 | 
			
		||||
export type ItemBody = {
 | 
			
		||||
  productId: number
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import { CommerceError } from '../../commerce/utils/errors'
 | 
			
		||||
import useWishlistAddItem from '../../commerce/wishlist/use-add-item'
 | 
			
		||||
import type { ItemBody, AddItemBody } from '../api/wishlist'
 | 
			
		||||
import useCustomer from '../use-customer'
 | 
			
		||||
import useWishlist, { Wishlist } from './use-wishlist'
 | 
			
		||||
import useWishlist, { UseWishlistOptions, Wishlist } from './use-wishlist'
 | 
			
		||||
 | 
			
		||||
const defaultOpts = {
 | 
			
		||||
  url: '/api/bigcommerce/wishlist',
 | 
			
		||||
@@ -27,9 +27,9 @@ export const fetcher: HookFetcher<Wishlist, AddItemBody> = (
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function extendHook(customFetcher: typeof fetcher) {
 | 
			
		||||
  const useAddItem = () => {
 | 
			
		||||
  const useAddItem = (opts?: UseWishlistOptions) => {
 | 
			
		||||
    const { data: customer } = useCustomer()
 | 
			
		||||
    const { mutate } = useWishlist()
 | 
			
		||||
    const { revalidate } = useWishlist(opts)
 | 
			
		||||
    const fn = useWishlistAddItem(defaultOpts, customFetcher)
 | 
			
		||||
 | 
			
		||||
    return useCallback(
 | 
			
		||||
@@ -42,10 +42,10 @@ export function extendHook(customFetcher: typeof fetcher) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const data = await fn({ item: input })
 | 
			
		||||
        await mutate(data, false)
 | 
			
		||||
        await revalidate()
 | 
			
		||||
        return data
 | 
			
		||||
      },
 | 
			
		||||
      [fn, mutate, customer]
 | 
			
		||||
      [fn, revalidate, customer]
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import { CommerceError } from '../../commerce/utils/errors'
 | 
			
		||||
import useWishlistRemoveItem from '../../commerce/wishlist/use-remove-item'
 | 
			
		||||
import type { RemoveItemBody } from '../api/wishlist'
 | 
			
		||||
import useCustomer from '../use-customer'
 | 
			
		||||
import useWishlist, { Wishlist } from './use-wishlist'
 | 
			
		||||
import useWishlist, { UseWishlistOptions, Wishlist } from './use-wishlist'
 | 
			
		||||
 | 
			
		||||
const defaultOpts = {
 | 
			
		||||
  url: '/api/bigcommerce/wishlist',
 | 
			
		||||
@@ -28,9 +28,9 @@ export const fetcher: HookFetcher<Wishlist | null, RemoveItemBody> = (
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function extendHook(customFetcher: typeof fetcher) {
 | 
			
		||||
  const useRemoveItem = () => {
 | 
			
		||||
  const useRemoveItem = (opts?: UseWishlistOptions) => {
 | 
			
		||||
    const { data: customer } = useCustomer()
 | 
			
		||||
    const { mutate } = useWishlist()
 | 
			
		||||
    const { revalidate } = useWishlist(opts)
 | 
			
		||||
    const fn = useWishlistRemoveItem<Wishlist | null, RemoveItemBody>(
 | 
			
		||||
      defaultOpts,
 | 
			
		||||
      customFetcher
 | 
			
		||||
@@ -46,10 +46,10 @@ export function extendHook(customFetcher: typeof fetcher) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const data = await fn({ itemId: String(input.id) })
 | 
			
		||||
        await mutate(data, false)
 | 
			
		||||
        await revalidate()
 | 
			
		||||
        return data
 | 
			
		||||
      },
 | 
			
		||||
      [fn, mutate, customer]
 | 
			
		||||
      [fn, revalidate, customer]
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@ export function extendHook(
 | 
			
		||||
    // response.data is also a getter and it's better to not trigger it early
 | 
			
		||||
    Object.defineProperty(response, 'isEmpty', {
 | 
			
		||||
      get() {
 | 
			
		||||
        return (response.data?.items?.length || 0) > 0
 | 
			
		||||
        return (response.data?.items?.length || 0) <= 0
 | 
			
		||||
      },
 | 
			
		||||
      set: (x) => x,
 | 
			
		||||
    })
 | 
			
		||||
 
 | 
			
		||||
@@ -1,64 +1,49 @@
 | 
			
		||||
import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next'
 | 
			
		||||
import type { GetStaticPropsContext } from 'next'
 | 
			
		||||
import getAllPages from '@lib/bigcommerce/api/operations/get-all-pages'
 | 
			
		||||
import useWishlist from '@lib/bigcommerce/wishlist/use-wishlist'
 | 
			
		||||
import { Layout } from '@components/core'
 | 
			
		||||
import { Heart } from '@components/icons'
 | 
			
		||||
import { Container, Text } from '@components/ui'
 | 
			
		||||
import { WishlistCard } from '@components/wishlist'
 | 
			
		||||
 | 
			
		||||
import getSiteInfo from '@lib/bigcommerce/api/operations/get-site-info'
 | 
			
		||||
 | 
			
		||||
export async function getStaticProps({ preview }: GetStaticPropsContext) {
 | 
			
		||||
  const { pages } = await getAllPages({ preview })
 | 
			
		||||
  const { categories, brands } = await getSiteInfo({ preview })
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    props: { pages, categories, brands },
 | 
			
		||||
    props: { pages },
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function Home({
 | 
			
		||||
  categories,
 | 
			
		||||
  brands,
 | 
			
		||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
 | 
			
		||||
  const { data } = useWishlist({ includeProducts: true })
 | 
			
		||||
  console.log(data)
 | 
			
		||||
export default function Wishlist() {
 | 
			
		||||
  const { data, isEmpty } = useWishlist({ includeProducts: true })
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Container>
 | 
			
		||||
      <div className="grid grid-cols-12 gap-8 mt-3 mb-20">
 | 
			
		||||
        <div className="col-span-2">
 | 
			
		||||
          <ul className="mb-10">
 | 
			
		||||
            <li className="py-1 text-base font-bold tracking-wide">
 | 
			
		||||
              All Categories
 | 
			
		||||
            </li>
 | 
			
		||||
            {categories.map((cat) => (
 | 
			
		||||
              <li key={cat.path} className="py-1 text-secondary">
 | 
			
		||||
                <a href="#">{cat.name}</a>
 | 
			
		||||
              </li>
 | 
			
		||||
            ))}
 | 
			
		||||
          </ul>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="col-span-8">
 | 
			
		||||
          <Text variant="pageHeading">My Wishlist</Text>
 | 
			
		||||
          <div className="group flex flex-col">
 | 
			
		||||
            {[1, 2, 3, 4, 5, 6].map((i) => (
 | 
			
		||||
              <WishlistCard key={i} />
 | 
			
		||||
            ))}
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="col-span-2">
 | 
			
		||||
          <ul>
 | 
			
		||||
            <li className="py-1 text-base font-bold tracking-wide">
 | 
			
		||||
              Relevance
 | 
			
		||||
            </li>
 | 
			
		||||
            <li className="py-1 text-secondary">Latest arrivals</li>
 | 
			
		||||
            <li className="py-1 text-secondary">Trending</li>
 | 
			
		||||
            <li className="py-1 text-secondary">Price: Low to high</li>
 | 
			
		||||
            <li className="py-1 text-secondary">Price: High to low</li>
 | 
			
		||||
          </ul>
 | 
			
		||||
      <div className="mt-3 mb-20">
 | 
			
		||||
        <Text variant="pageHeading">My Wishlist</Text>
 | 
			
		||||
        <div className="group flex flex-col">
 | 
			
		||||
          {isEmpty ? (
 | 
			
		||||
            <div className="flex-1 px-12 py-24 flex flex-col justify-center items-center ">
 | 
			
		||||
              <span className="border border-dashed border-secondary rounded-full flex items-center justify-center w-16 h-16 bg-primary p-12 rounded-lg text-primary">
 | 
			
		||||
                <Heart className="absolute" />
 | 
			
		||||
              </span>
 | 
			
		||||
              <h2 className="pt-6 text-2xl font-bold tracking-wide text-center">
 | 
			
		||||
                Your wishlist is empty
 | 
			
		||||
              </h2>
 | 
			
		||||
              <p className="text-accents-6 px-10 text-center pt-2">
 | 
			
		||||
                Biscuit oat cake wafer icing ice cream tiramisu pudding cupcake.
 | 
			
		||||
              </p>
 | 
			
		||||
            </div>
 | 
			
		||||
          ) : (
 | 
			
		||||
            data &&
 | 
			
		||||
            data.items?.map((item) => (
 | 
			
		||||
              <WishlistCard key={item.id} item={item} />
 | 
			
		||||
            ))
 | 
			
		||||
          )}
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </Container>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Home.Layout = Layout
 | 
			
		||||
Wishlist.Layout = Layout
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user