mirror of
https://github.com/vercel/commerce.git
synced 2025-05-17 23:16:59 +00:00
modified commerce wishlist endpoint to extract item variable from req/body, added useAdminApi flag to commerce fetcherOptions, updated wishlist api constants, modified shopify fetcher, added wishlist handlers to shopify provider Polished frontend UI so that you can now correctly add product to wishlist if variant is not available for sale
This commit is contained in:
parent
890836bc24
commit
594b3ef16a
@ -34,13 +34,13 @@ const wishlistEndpoint: GetAPISchema<
|
||||
|
||||
// Add an item to the wishlist
|
||||
if (req.method === 'POST') {
|
||||
const body = { ...req.body, customerToken }
|
||||
const body = { ...req.body.variables, customerToken }
|
||||
return await handlers['addItem']({ ...ctx, body })
|
||||
}
|
||||
|
||||
// Remove an item from the wishlist
|
||||
if (req.method === 'DELETE') {
|
||||
const body = { ...req.body, customerToken }
|
||||
const body = { ...req.body.variables, customerToken }
|
||||
return await handlers['removeItem']({ ...ctx, body })
|
||||
}
|
||||
} catch (error) {
|
||||
|
@ -27,6 +27,7 @@ export type FetcherOptions<Body = any> = {
|
||||
method?: string
|
||||
variables?: any
|
||||
body?: Body
|
||||
useAdminApi?: boolean
|
||||
}
|
||||
|
||||
export type HookFetcher<Data, Input = null, Result = any> = (
|
||||
|
@ -5,7 +5,8 @@ import {
|
||||
} from '@vercel/commerce/api'
|
||||
|
||||
import {
|
||||
API_URL,
|
||||
STOREFRONT_API_URL,
|
||||
ADMIN_ACCESS_TOKEN,
|
||||
API_TOKEN,
|
||||
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
||||
SHOPIFY_CHECKOUT_ID_COOKIE,
|
||||
@ -15,7 +16,7 @@ import fetchGraphqlApi from './utils/fetch-graphql-api'
|
||||
|
||||
import * as operations from './operations'
|
||||
|
||||
if (!API_URL) {
|
||||
if (!STOREFRONT_API_URL) {
|
||||
throw new Error(
|
||||
`The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store`
|
||||
)
|
||||
@ -31,7 +32,7 @@ export interface ShopifyConfig extends CommerceAPIConfig {}
|
||||
const ONE_DAY = 60 * 60 * 24
|
||||
|
||||
const config: ShopifyConfig = {
|
||||
commerceUrl: API_URL,
|
||||
commerceUrl: STOREFRONT_API_URL,
|
||||
apiToken: API_TOKEN,
|
||||
customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
||||
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
|
||||
|
@ -1,16 +1,47 @@
|
||||
import type { GraphQLFetcher } from '@vercel/commerce/api'
|
||||
import fetch from './fetch'
|
||||
|
||||
import { API_URL, API_TOKEN } from '../../const'
|
||||
import {
|
||||
STOREFRONT_API_URL,
|
||||
ADMIN_API_URL,
|
||||
API_TOKEN,
|
||||
ADMIN_ACCESS_TOKEN,
|
||||
} from '../../const'
|
||||
import { getError } from '../../utils/handle-fetch-response'
|
||||
|
||||
const fetchGraphqlApi: GraphQLFetcher = async (
|
||||
query: string,
|
||||
{ variables } = {},
|
||||
fetchOptions
|
||||
fetchOptions,
|
||||
useAdminApi = false
|
||||
) => {
|
||||
try {
|
||||
const res = await fetch(API_URL, {
|
||||
if (useAdminApi) {
|
||||
console.log('graphQL fetch from admin api')
|
||||
|
||||
const res = await fetch(ADMIN_API_URL, {
|
||||
...fetchOptions,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Shopify-Access-Token': ADMIN_ACCESS_TOKEN!,
|
||||
...fetchOptions?.headers,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
variables,
|
||||
}),
|
||||
})
|
||||
const { data, errors, status } = await res.json()
|
||||
if (errors) {
|
||||
throw getError(errors, status)
|
||||
}
|
||||
|
||||
return { data, res }
|
||||
} else {
|
||||
console.log('graphQL fetch from storefront api')
|
||||
|
||||
const res = await fetch(STOREFRONT_API_URL, {
|
||||
...fetchOptions,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@ -23,14 +54,13 @@ const fetchGraphqlApi: GraphQLFetcher = async (
|
||||
variables,
|
||||
}),
|
||||
})
|
||||
|
||||
const { data, errors, status } = await res.json()
|
||||
|
||||
if (errors) {
|
||||
throw getError(errors, status)
|
||||
}
|
||||
|
||||
return { data, res }
|
||||
}
|
||||
} catch (err) {
|
||||
throw getError(
|
||||
[
|
||||
|
@ -8,6 +8,9 @@ export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN
|
||||
|
||||
export const SHOPIFY_COOKIE_EXPIRE = 30
|
||||
|
||||
export const API_URL = `https://${STORE_DOMAIN}/api/2021-07/graphql.json`
|
||||
export const STOREFRONT_API_URL = `https://${STORE_DOMAIN}/api/2022-01/graphql.json`
|
||||
export const ADMIN_API_URL = `https://${STORE_DOMAIN}/admin/api/2022-01/graphql.json`
|
||||
|
||||
export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN
|
||||
export const ADMIN_ACCESS_TOKEN =
|
||||
process.env.NEXT_PUBLIC_SHOPIFY_ADMIN_ACCESS_TOKEN
|
||||
|
@ -1,14 +1,41 @@
|
||||
import { Fetcher } from '@vercel/commerce/utils/types'
|
||||
import { API_TOKEN, API_URL } from './const'
|
||||
import {
|
||||
API_TOKEN,
|
||||
STOREFRONT_API_URL,
|
||||
ADMIN_ACCESS_TOKEN,
|
||||
ADMIN_API_URL,
|
||||
} from './const'
|
||||
import { handleFetchResponse } from './utils'
|
||||
|
||||
const fetcher: Fetcher = async ({
|
||||
url = API_URL,
|
||||
url = STOREFRONT_API_URL,
|
||||
method = 'POST',
|
||||
variables,
|
||||
query,
|
||||
useAdminApi = false,
|
||||
}) => {
|
||||
const { locale, ...vars } = variables ?? {}
|
||||
|
||||
if (method === 'POST' || method === 'DELETE') {
|
||||
if (useAdminApi) {
|
||||
url = ADMIN_API_URL
|
||||
console.log('admin api', url, query, method)
|
||||
|
||||
return handleFetchResponse(
|
||||
await fetch(url, {
|
||||
method,
|
||||
body: JSON.stringify({ query, variables: vars }),
|
||||
headers: {
|
||||
'X-Shopify-Access-Token': ADMIN_ACCESS_TOKEN!,
|
||||
'Content-Type': 'application/json',
|
||||
...(locale && {
|
||||
'Accept-Language': locale,
|
||||
}),
|
||||
},
|
||||
})
|
||||
)
|
||||
} else {
|
||||
console.log('storefront api:', url, query, method)
|
||||
return handleFetchResponse(
|
||||
await fetch(url, {
|
||||
method,
|
||||
@ -23,5 +50,9 @@ const fetcher: Fetcher = async ({
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return handleFetchResponse(await fetch(url))
|
||||
}
|
||||
|
||||
export default fetcher
|
||||
|
@ -12,6 +12,10 @@ import { handler as useLogin } from './auth/use-login'
|
||||
import { handler as useLogout } from './auth/use-logout'
|
||||
import { handler as useSignup } from './auth/use-signup'
|
||||
|
||||
import { handler as useWishlist } from './wishlist/use-wishlist'
|
||||
import { handler as useWishlistAddItem } from './wishlist/use-add-item'
|
||||
import { handler as useWishlistRemoveItem } from './wishlist/use-remove-item'
|
||||
|
||||
import fetcher from './fetcher'
|
||||
|
||||
export const shopifyProvider = {
|
||||
@ -22,6 +26,11 @@ export const shopifyProvider = {
|
||||
customer: { useCustomer },
|
||||
products: { useSearch },
|
||||
auth: { useLogin, useLogout, useSignup },
|
||||
wishlist: {
|
||||
useWishlist,
|
||||
useAddItem: useWishlistAddItem,
|
||||
useRemoveItem: useWishlistRemoveItem,
|
||||
},
|
||||
}
|
||||
|
||||
export type ShopifyProvider = typeof shopifyProvider
|
||||
|
@ -19,12 +19,6 @@ export type WishlistTypes = {
|
||||
customer: Customer
|
||||
}
|
||||
|
||||
export type RemoveItemHook<T extends WishlistTypes = WishlistTypes> = {
|
||||
body: { item: T['itemBody'] }
|
||||
fetcherInput: { item: T['itemBody'] }
|
||||
actionInput: T['itemBody']
|
||||
}
|
||||
|
||||
export type WishlistSchema = Core.WishlistSchema<WishlistTypes>
|
||||
export type GetCustomerWishlistOperation =
|
||||
Core.GetCustomerWishlistOperation<WishlistTypes>
|
||||
|
@ -16,6 +16,33 @@ export const productConnectionFragment = /* GraphQL */ `
|
||||
currencyCode
|
||||
}
|
||||
}
|
||||
variants(first: 250) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
title
|
||||
sku
|
||||
availableForSale
|
||||
requiresShipping
|
||||
selectedOptions {
|
||||
name
|
||||
value
|
||||
}
|
||||
priceV2 {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
compareAtPriceV2 {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
images(first: 1) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
|
3
packages/shopify/src/wishlist/index.ts
Normal file
3
packages/shopify/src/wishlist/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { default as useAddItem } from './use-add-item'
|
||||
export { default as useWishlist } from './use-wishlist'
|
||||
export { default as useRemoveItem } from './use-remove-item'
|
@ -1,13 +1,46 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { useCallback } from 'react'
|
||||
import { CommerceError } from '@vercel/commerce/utils/errors'
|
||||
import useAddItem, { UseAddItem } from '@vercel/commerce/wishlist/use-add-item'
|
||||
import type { AddItemHook } from '../types/wishlist'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
import useWishlist from './use-wishlist'
|
||||
import type { MutationHook } from '@vercel/commerce/utils/types'
|
||||
|
||||
export function emptyHook() {
|
||||
const useEmptyHook = async (options = {}) => {
|
||||
return useCallback(async function () {
|
||||
return Promise.resolve()
|
||||
}, [])
|
||||
export default useAddItem as UseAddItem<typeof handler>
|
||||
|
||||
export const handler: MutationHook<AddItemHook> = {
|
||||
fetchOptions: {
|
||||
url: '/api/wishlist',
|
||||
method: 'POST',
|
||||
},
|
||||
async fetcher({ input: item, options, fetch }) {
|
||||
const data = await fetch({ ...options, variables: item })
|
||||
|
||||
return data
|
||||
},
|
||||
useHook:
|
||||
({ fetch }) =>
|
||||
() => {
|
||||
const { data: customer } = useCustomer()
|
||||
const { mutate } = 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',
|
||||
})
|
||||
}
|
||||
|
||||
return useEmptyHook
|
||||
}
|
||||
// TODO: add validations before doing the fetch
|
||||
|
||||
export default emptyHook
|
||||
const data = await fetch({ input: { item } })
|
||||
await mutate()
|
||||
return data
|
||||
},
|
||||
[fetch, mutate, customer]
|
||||
)
|
||||
},
|
||||
}
|
||||
|
@ -1,17 +1,48 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { CommerceError } from '@vercel/commerce/utils/errors'
|
||||
import type { RemoveItemHook } from '../types/wishlist'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
import useWishlist from './use-wishlist'
|
||||
import type { MutationHook } from '@vercel/commerce/utils/types'
|
||||
import useRemoveItem, {
|
||||
UseRemoveItem,
|
||||
} from '@vercel/commerce/wishlist/use-remove-item'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
type Options = {
|
||||
includeProducts?: boolean
|
||||
export default useRemoveItem as UseRemoveItem<typeof handler>
|
||||
|
||||
export const handler: MutationHook<RemoveItemHook> = {
|
||||
fetchOptions: {
|
||||
url: '/api/wishlist',
|
||||
method: 'DELETE',
|
||||
},
|
||||
async fetcher({ input: item, options, fetch }) {
|
||||
const data = await fetch({ ...options, variables: item })
|
||||
|
||||
return data
|
||||
},
|
||||
useHook:
|
||||
({ fetch }) =>
|
||||
() => {
|
||||
const { data: customer } = useCustomer()
|
||||
const { mutate } = useWishlist()
|
||||
|
||||
return useCallback(
|
||||
async function removeItem(item) {
|
||||
if (!customer) {
|
||||
// A signed customer is required in order to have a wishlist
|
||||
throw new CommerceError({
|
||||
message: 'Signed customer not found',
|
||||
})
|
||||
}
|
||||
|
||||
export function emptyHook(options?: Options) {
|
||||
const useEmptyHook = async ({ id }: { id: string | number }) => {
|
||||
return useCallback(async function () {
|
||||
return Promise.resolve()
|
||||
}, [])
|
||||
}
|
||||
// TODO: add validations before doing the fetch
|
||||
|
||||
return useEmptyHook
|
||||
const data = await fetch({ input: { item } })
|
||||
await mutate()
|
||||
return data
|
||||
},
|
||||
[fetch, mutate, customer]
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default emptyHook
|
||||
|
@ -1,46 +1,46 @@
|
||||
// TODO: replace this hook and other wishlist hooks with a handler, or remove them if
|
||||
// Shopify doesn't have a wishlist
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import useWishlist, {
|
||||
UseWishlist,
|
||||
} from '@vercel/commerce/wishlist/use-wishlist'
|
||||
import type { GetWishlistHook } from '../types/wishlist'
|
||||
import { SWRHook } from '@vercel/commerce/utils/types'
|
||||
|
||||
import { HookFetcher } from '@vercel/commerce/utils/types'
|
||||
import { Product } from '../../schema'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
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<GetWishlistHook> = {
|
||||
fetchOptions: {
|
||||
url: '/api/wishlist',
|
||||
method: 'GET',
|
||||
},
|
||||
async fetcher({ options, fetch }) {
|
||||
const data = await fetch({ ...options })
|
||||
|
||||
return data
|
||||
},
|
||||
|
||||
useHook:
|
||||
({ useData }) =>
|
||||
(input) => {
|
||||
const response = useData({
|
||||
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)
|
||||
|
@ -105,7 +105,7 @@ const ProductCard: FC<Props> = ({
|
||||
<WishlistButton
|
||||
className={s.wishlistButton}
|
||||
productId={product.id}
|
||||
variant={product.variants[0] as any}
|
||||
variant={product.variants[0]}
|
||||
/>
|
||||
)}
|
||||
<ProductTag
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
selectDefaultOptionFromProduct,
|
||||
SelectedOptions,
|
||||
} from '../helpers'
|
||||
import { WishlistButton } from '@components/wishlist'
|
||||
|
||||
interface ProductSidebarProps {
|
||||
product: Product
|
||||
@ -70,6 +71,13 @@ const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
|
||||
: 'Add To Cart'}
|
||||
</Button>
|
||||
)}
|
||||
{!variant?.availableForSale && (
|
||||
<WishlistButton
|
||||
className={s.button}
|
||||
productId={product.id}
|
||||
variant={variant!}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-6">
|
||||
<Collapse title="Care">
|
||||
|
@ -2,10 +2,10 @@ import React, { FC, useState } from 'react'
|
||||
import cn from 'clsx'
|
||||
import { useUI } from '@components/ui'
|
||||
import { Heart } from '@components/icons'
|
||||
import useAddItem from '@framework/wishlist/use-add-item'
|
||||
import { useAddItem } from '@framework/wishlist'
|
||||
import useCustomer from '@framework/customer/use-customer'
|
||||
import useWishlist from '@framework/wishlist/use-wishlist'
|
||||
import useRemoveItem from '@framework/wishlist/use-remove-item'
|
||||
import { useWishlist } from '@framework/wishlist'
|
||||
import { useRemoveItem } from '@framework/wishlist'
|
||||
import s from './WishlistButton.module.css'
|
||||
import type { Product, ProductVariant } from '@commerce/types/product'
|
||||
|
||||
@ -30,9 +30,7 @@ const WishlistButton: FC<Props> = ({
|
||||
// @ts-ignore Wishlist is not always enabled
|
||||
const itemInWishlist = data?.items?.find(
|
||||
// @ts-ignore Wishlist is not always enabled
|
||||
(item) =>
|
||||
item.product_id === Number(productId) &&
|
||||
item.variant_id === Number(variant.id)
|
||||
(item) => item.productId === productId && item.variantId === variant.id
|
||||
)
|
||||
|
||||
const handleWishlistChange = async (e: any) => {
|
||||
@ -50,7 +48,7 @@ const WishlistButton: FC<Props> = ({
|
||||
|
||||
try {
|
||||
if (itemInWishlist) {
|
||||
await removeItem({ id: itemInWishlist.id! })
|
||||
await removeItem({ productId, variantId: variant?.id! })
|
||||
} else {
|
||||
await addItem({
|
||||
productId,
|
||||
|
@ -15,17 +15,19 @@ import type { Wishlist } from '@commerce/types/wishlist'
|
||||
|
||||
const placeholderImg = '/product-img-placeholder.svg'
|
||||
|
||||
const WishlistCard: React.FC<{
|
||||
item: Wishlist
|
||||
}> = ({ item }) => {
|
||||
const product: Product = item.product
|
||||
interface Props {
|
||||
item: Product
|
||||
variant: string | number
|
||||
}
|
||||
|
||||
const WishlistCard: FC<Props> = ({ item, variant }) => {
|
||||
const { price } = usePrice({
|
||||
amount: product.price?.value,
|
||||
baseAmount: product.price?.retailPrice,
|
||||
currencyCode: product.price?.currencyCode!,
|
||||
amount: item.price?.value,
|
||||
baseAmount: item.price?.retailPrice,
|
||||
currencyCode: item.price?.currencyCode!,
|
||||
})
|
||||
// @ts-ignore Wishlist is not always enabled
|
||||
const removeItem = useRemoveItem({ wishlist: { includeProducts: true } })
|
||||
const removeItem = useRemoveItem({ item })
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [removing, setRemoving] = useState(false)
|
||||
|
||||
@ -40,7 +42,7 @@ const WishlistCard: React.FC<{
|
||||
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! })
|
||||
await removeItem({ productId: item.id, variantId: variant })
|
||||
} catch (error) {
|
||||
setRemoving(false)
|
||||
}
|
||||
@ -49,8 +51,8 @@ const WishlistCard: React.FC<{
|
||||
setLoading(true)
|
||||
try {
|
||||
await addItem({
|
||||
productId: String(product.id),
|
||||
variantId: String(product.variants[0].id),
|
||||
productId: String(item.id),
|
||||
variantId: String(item.variants[0].id),
|
||||
})
|
||||
openSidebar()
|
||||
setLoading(false)
|
||||
@ -65,20 +67,20 @@ const WishlistCard: React.FC<{
|
||||
<Image
|
||||
width={230}
|
||||
height={230}
|
||||
src={product.images[0]?.url || placeholderImg}
|
||||
alt={product.images[0]?.alt || 'Product Image'}
|
||||
src={item.images[0]?.url || placeholderImg}
|
||||
alt={item.images[0]?.alt || 'Product Image'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={s.description}>
|
||||
<div className="flex-1 mb-6">
|
||||
<h3 className="text-2xl mb-2 -mt-1">
|
||||
<Link href={`/product${product.path}`}>
|
||||
<a>{product.name}</a>
|
||||
<Link href={`/product${item.path}`}>
|
||||
<a>{item.name}</a>
|
||||
</Link>
|
||||
</h3>
|
||||
<div className="mb-4">
|
||||
<Text html={product.description} />
|
||||
<Text html={item.description} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
@ -37,7 +37,7 @@ export async function getStaticProps({
|
||||
export default function Wishlist() {
|
||||
const { data: customer } = useCustomer()
|
||||
// @ts-ignore Shopify - Fix this types
|
||||
const { data, isLoading, isEmpty } = useWishlist({ includeProducts: true })
|
||||
const { data: wishlist, isLoading, isEmpty } = useWishlist()
|
||||
|
||||
return (
|
||||
<Container className="pt-4">
|
||||
@ -66,10 +66,14 @@ export default function Wishlist() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 gap-6 ">
|
||||
{data &&
|
||||
{wishlist &&
|
||||
// @ts-ignore - Wishlist Item Type
|
||||
data.items?.map((item) => (
|
||||
<WishlistCard key={item.id} item={item!} />
|
||||
wishlist.items?.map((item) => (
|
||||
<WishlistCard
|
||||
key={item.productId}
|
||||
item={item.product!}
|
||||
variant={item.variantId}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
Loading…
x
Reference in New Issue
Block a user