feat: Update hooks

This commit is contained in:
Alessandro Casazza 2022-05-10 18:57:21 +02:00
parent a9e2d75f14
commit f11ff29e3b
No known key found for this signature in database
GPG Key ID: 3AF41B06C6495D3D
10 changed files with 239 additions and 167 deletions

View File

@ -1,54 +1,52 @@
import useAddItem, { UseAddItem } from '@vercel/commerce/cart/use-add-item' import useAddItem, { UseAddItem } from '@vercel/commerce/cart/use-add-item'
import { MutationHook } from '@vercel/commerce/utils/types' import { MutationHook } from '@vercel/commerce/utils/types'
import { LineItem, Order } from '@commercelayer/js-sdk' import CLSdk from '@commercelayer/sdk'
import getCredentials from '../api/utils/getCredentials' import getCredentials, {
getOrganizationSlug,
} from '../api/utils/getCredentials'
import useCart from '../cart/use-cart' import useCart from '../cart/use-cart'
import { useCallback } from 'react' import { useCallback } from 'react'
import getContentData from '../api/utils/getContentData'
export default useAddItem as UseAddItem<typeof handler> export default useAddItem as UseAddItem<typeof handler>
export const handler: MutationHook<any> = { export const handler: MutationHook<any> = {
fetchOptions: { fetchOptions: {
query: '', query: '',
}, },
async fetcher({ input, options, fetch }) { async fetcher({ input }) {
const localOrderId = localStorage.getItem('CL_ORDER_ID') const localOrderId = localStorage.getItem('CL_ORDER_ID')
const credentials = getCredentials() const { accessToken, endpoint } = getCredentials()
const organization = getOrganizationSlug(endpoint).organization
const sdk = CLSdk({
accessToken,
organization,
})
const orderId = const orderId =
localOrderId || localOrderId || (accessToken && (await sdk.orders.create({})).id)
(credentials.accessToken &&
(await Order.withCredentials(credentials).create({})).id)
if (orderId && input.variantId) { if (orderId && input.variantId) {
!localOrderId && localStorage.setItem('CL_ORDER_ID', orderId) !localOrderId && localStorage.setItem('CL_ORDER_ID', orderId)
const lineItem = await LineItem.withCredentials(credentials).create( const [product] = await getContentData(input.productId)
{ const [image] = product.images
skuCode: input.variantId, const lineItem = await sdk.line_items.create({
order: Order.build({ id: orderId }), sku_code: input.variantId,
order: sdk.orders.relationship(orderId),
quantity: 1, quantity: 1,
reference: input.productId, reference: input.productId,
_update_quantity: 1, _update_quantity: true,
}, })
// @ts-ignore
{ rawResponse: true }
)
const attributes = lineItem.data.attributes
return { return {
id: lineItem.data.id, id: lineItem.id,
name: attributes.name, name: lineItem.name,
productId: input.productId, productId: input.productId,
variantId: input.variantId, variantId: input.variantId,
quantity: attributes.quantity, quantity: lineItem.quantity,
price: attributes.unit_amount_float, price: lineItem.unit_amount_float,
variant: { variant: {
id: lineItem.data.id, id: lineItem.id,
name: attributes.name, name: lineItem.name,
sku: input.variantId, sku: input.variantId,
price: attributes.unit_amount_float, price: lineItem.unit_amount_float,
image: { image,
url: `https://data.commercelayer.app/vercel-provider/${input.productId}_FLAT.png`,
altText: attributes.name,
width: 1000,
height: 1000,
},
}, },
} }
} }
@ -57,6 +55,7 @@ export const handler: MutationHook<any> = {
({ fetch }) => ({ fetch }) =>
() => { () => {
const { mutate } = useCart() const { mutate } = useCart()
return useCallback( return useCallback(
async function addItem(input) { async function addItem(input) {
const data = await fetch({ input }) const data = await fetch({ input })

View File

@ -1,8 +1,10 @@
import { useMemo } from 'react' import { useMemo } from 'react'
import { SWRHook } from '@vercel/commerce/utils/types' import { SWRHook } from '@vercel/commerce/utils/types'
import useCart, { UseCart } from '@vercel/commerce/cart/use-cart' import useCart, { UseCart } from '@vercel/commerce/cart/use-cart'
import { Order } from '@commercelayer/js-sdk' import CLSdk from '@commercelayer/sdk'
import getCredentials from '../api/utils/getCredentials' import getCredentials, {
getOrganizationSlug,
} from '../api/utils/getCredentials'
import normalizeLineItems from '../api/utils/normalizeLineItems' import normalizeLineItems from '../api/utils/normalizeLineItems'
export default useCart as UseCart<typeof handler> export default useCart as UseCart<typeof handler>
@ -14,25 +16,27 @@ export const handler: SWRHook<any> = {
async fetcher() { async fetcher() {
const id = localStorage.getItem('CL_ORDER_ID') || '' const id = localStorage.getItem('CL_ORDER_ID') || ''
const credentials = getCredentials() const credentials = getCredentials()
const organization = getOrganizationSlug(credentials.endpoint).organization
const sdk = CLSdk({
accessToken: credentials.accessToken,
organization,
})
if (id && credentials.accessToken) { if (id && credentials.accessToken) {
const clOrder = await Order.withCredentials(credentials) const order = await sdk.orders.retrieve(id, { include: ['line_items'] })
.includes('lineItems') const orderStatus = order.status
.find(id, { rawResponse: true }) if (orderStatus && ['pending', 'draft'].includes(orderStatus)) {
const attributes = clOrder.data.attributes const lineItems = order.line_items
const orderStatus = attributes.status ? normalizeLineItems(order.line_items)
if (['pending', 'draft'].includes(orderStatus)) {
const lineItems = clOrder?.included
? normalizeLineItems(clOrder?.included)
: [] : []
return { return {
id, id,
createdAt: attributes.created_at, createdAt: order.created_at,
currency: { code: attributes.currency_code }, currency: { code: order.currency_code },
taxesIncluded: '', taxesIncluded: '',
lineItems, lineItems,
lineItemsSubtotalPrice: '', lineItemsSubtotalPrice: '',
subtotalPrice: attributes.subtotal_amount_float, subtotalPrice: order.subtotal_amount_float,
totalPrice: attributes.total_amount_float, totalPrice: order.total_amount_float,
} }
} else if (id) { } else if (id) {
localStorage.removeItem('CL_ORDER_ID') localStorage.removeItem('CL_ORDER_ID')
@ -53,6 +57,7 @@ export const handler: SWRHook<any> = {
({ useData }) => ({ useData }) =>
() => { () => {
const response = useData() const response = useData()
console.log('response', response)
return useMemo( return useMemo(
() => () =>
Object.create(response, { Object.create(response, {

View File

@ -1,7 +1,11 @@
import { MutationHook } from '@vercel/commerce/utils/types' import { MutationHook } from '@vercel/commerce/utils/types'
import useRemoveItem, { UseRemoveItem } from '@vercel/commerce/cart/use-remove-item' import useRemoveItem, {
import getCredentials from '../api/utils/getCredentials' UseRemoveItem,
import { LineItem } from '@commercelayer/js-sdk' } from '@vercel/commerce/cart/use-remove-item'
import getCredentials, {
getOrganizationSlug,
} from '../api/utils/getCredentials'
import CLSdk from '@commercelayer/sdk'
import useCart from './use-cart' import useCart from './use-cart'
export default useRemoveItem as UseRemoveItem<typeof handler> export default useRemoveItem as UseRemoveItem<typeof handler>
@ -13,8 +17,13 @@ export const handler: MutationHook<any> = {
async fetcher({ input: { id } }) { async fetcher({ input: { id } }) {
const credentials = getCredentials() const credentials = getCredentials()
const orderId = localStorage.getItem('CL_ORDER_ID') const orderId = localStorage.getItem('CL_ORDER_ID')
const organization = getOrganizationSlug(credentials.endpoint).organization
const sdk = CLSdk({
accessToken: credentials.accessToken,
organization,
})
if (orderId && id) { if (orderId && id) {
await LineItem.build({ id }).withCredentials(credentials).destroy() await sdk.line_items.delete(id)
return {} return {}
} }
}, },

View File

@ -1,8 +1,13 @@
import { MutationHook } from '@vercel/commerce/utils/types' import { MutationHook } from '@vercel/commerce/utils/types'
import useUpdateItem, { UseUpdateItem } from '@vercel/commerce/cart/use-update-item' import useUpdateItem, {
UseUpdateItem,
} from '@vercel/commerce/cart/use-update-item'
import useCart from '../cart/use-cart' import useCart from '../cart/use-cart'
import getCredentials from '../api/utils/getCredentials' import getCredentials, {
import { LineItem } from '@commercelayer/js-sdk' getOrganizationSlug,
} from '../api/utils/getCredentials'
import CLSdk from '@commercelayer/sdk'
import getContentData from '../api/utils/getContentData'
export default useUpdateItem as UseUpdateItem<any> export default useUpdateItem as UseUpdateItem<any>
@ -12,33 +17,29 @@ export const handler: MutationHook<any> = {
}, },
async fetcher({ input: { item, quantity } }) { async fetcher({ input: { item, quantity } }) {
const credentials = getCredentials() const credentials = getCredentials()
const organization = getOrganizationSlug(credentials.endpoint).organization
const sdk = CLSdk({
accessToken: credentials.accessToken,
organization,
})
const orderId = localStorage.getItem('CL_ORDER_ID') const orderId = localStorage.getItem('CL_ORDER_ID')
if (orderId && item.id) { if (orderId && item.id) {
const lineItem = (await LineItem.build({ const lineItem = await sdk.line_items.update({ id: item.id, quantity })
id: item.id, const [product] = await getContentData(item.productId)
}) const [image] = product.images
.withCredentials(credentials)
// @ts-ignore
.update({ quantity }, null, { rawResponse: true })) as any
const attributes = lineItem.data.attributes
return { return {
id: lineItem.data.id, id: lineItem.id,
name: attributes.name, name: lineItem.name,
productId: item.productId, productId: item.productId,
variantId: item.variantId, variantId: item.variantId,
quantity: attributes.quantity, quantity: lineItem.quantity,
price: attributes.unit_amount_float, price: lineItem.unit_amount_float,
variant: { variant: {
id: lineItem.data.id, id: lineItem.id,
name: attributes.name, name: lineItem.name,
sku: lineItem.data.sku_code, sku: lineItem.sku_code,
price: attributes.unit_amount_float, price: lineItem.unit_amount_float,
image: { image,
url: `https://data.commercelayer.app/vercel-provider/${item.variantId}_FLAT.png`,
altText: 'Black Women Long Sleeve Shirt',
width: 1000,
height: 1000,
},
}, },
} }
} }

View File

@ -1,4 +1,3 @@
import * as React from 'react'
import { ReactNode } from 'react' import { ReactNode } from 'react'
import { CommercelayerProvider } from './provider' import { CommercelayerProvider } from './provider'
import { import {

View File

@ -1,19 +1,20 @@
import { SWRHook } from '@vercel/commerce/utils/types' import { SWRHook } from '@vercel/commerce/utils/types'
import useSearch, { UseSearch } from '@vercel/commerce/product/use-search' import useSearch, { UseSearch } from '@vercel/commerce/product/use-search'
import data from '../data.json' import getContentData from '../api/utils/getContentData'
import type { Products } from '../api/utils/getContentData'
export default useSearch as UseSearch<typeof handler> export default useSearch as UseSearch<typeof handler>
const productsFinder = (s: string, c?: string, b?: string) => { const productsFinder = (
const { products } = data products: Products,
s: string,
c?: string,
b?: string
) => {
let p = products let p = products
if (s) if (s) p = p.filter((p) => p.name.toLowerCase().includes(s.toLowerCase()))
p = p.filter((p) => p.name.toLowerCase().search(s.toLowerCase()) !== -1)
if (c) if (c)
p = p.filter( p = p.filter((p) => p.categoryId.toLowerCase().includes(c.toLowerCase()))
(p) => p.categoryId.toLowerCase().search(c.toLowerCase()) !== -1 if (b) p = p.filter((p) => p.brandId.toLowerCase().includes(b.toLowerCase()))
)
if (b)
p = p.filter((p) => p.brandId.toLowerCase().search(b.toLowerCase()) !== -1)
return p return p
} }
@ -21,19 +22,33 @@ export const handler: SWRHook<any> = {
fetchOptions: { fetchOptions: {
query: '', query: '',
}, },
async fetcher({ input, options, fetch }) {}, async fetcher({ input }) {
useHook: const { search, categoryId, brandId } = input
({ useData }) => const contentData = await getContentData()
({ search, categoryId, brandId }) => { const products = productsFinder(contentData, search, categoryId, brandId)
const products = productsFinder(search, categoryId, brandId) return products.length > 0
return {
data:
products.length > 0
? { ? {
products, products,
found: true, found: true,
} }
: data, : {
products: contentData,
} }
}, },
useHook:
({ useData }) =>
(input = {}) => {
return useData({
input: [
['search', input.search],
['categoryId', input.categoryId],
['brandId', input.brandId],
['sort', input.sort],
],
swrOptions: {
revalidateOnFocus: false,
...input.swrOptions,
},
})
},
} }

View File

@ -8,6 +8,9 @@ import { handler as useSearch } from './product/use-search'
import { handler as useLogin } from './auth/use-login' import { handler as useLogin } from './auth/use-login'
import { handler as useLogout } from './auth/use-logout' import { handler as useLogout } from './auth/use-logout'
import { handler as useSignup } from './auth/use-signup' 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'
export const CommercelayerProvider = { export const CommercelayerProvider = {
locale: 'en-US', locale: 'en-US',
@ -18,6 +21,11 @@ export const CommercelayerProvider = {
customer: { useCustomer }, customer: { useCustomer },
products: { useSearch }, products: { useSearch },
auth: { useLogin, useLogout, useSignup }, auth: { useLogin, useLogout, useSignup },
wishlist: {
useWishlist,
useAddItem: useWishlistAddItem,
useRemoveItem: useWishlistRemoveItem,
},
} }
export type Provider = typeof CommercelayerProvider export type Provider = typeof CommercelayerProvider

View File

@ -1,22 +1,48 @@
import { useCallback, useMemo } from 'react' import useAddItem, { UseAddItem } from '@vercel/commerce/wishlist/use-add-item'
import useCustomer from '../customer/use-customer'
import { useCallback } from 'react'
import { MutationHook } from '@vercel/commerce/utils/types'
import useWishlist from './use-wishlist'
import { CommerceError } from '@vercel/commerce/utils/errors'
export default useAddItem as UseAddItem<typeof handler>
export function emptyHook() { export const handler: MutationHook<any> = {
const useEmptyHook = async (options: any = {}) => { fetchOptions: {
query: '',
},
async fetcher({ input }) {
const { variantId } = input
let wishlist = [] let wishlist = []
const localWishlist = localStorage.getItem('wishlist') const localWishlist = localStorage.getItem('wishlist')
if (localWishlist) { if (localWishlist) {
wishlist = JSON.parse(localWishlist) wishlist = JSON.parse(localWishlist)
if (!wishlist.includes(options.variantId)) { if (!wishlist.includes(variantId)) {
wishlist.push(options.variantId) wishlist.push(variantId)
} }
} else { } else {
wishlist.push(options.variantId) wishlist.push(variantId)
} }
localStorage.setItem('wishlist', JSON.stringify(wishlist)) localStorage.setItem('wishlist', JSON.stringify(wishlist))
return wishlist return wishlist
},
useHook:
({ fetch }) =>
() => {
const { mutate } = useWishlist()
const { data: customer } = useCustomer()
return useCallback(
async function addItem(input) {
if (!customer) {
// A signed customer is required in order to have a wishlist
throw new CommerceError({
message: 'Signed customer not found',
})
} }
const data = await fetch({ input })
return useEmptyHook await mutate()
return data
},
[fetch]
)
},
} }
export default emptyHook

View File

@ -1,20 +1,46 @@
type Options = { import useRemoveItem, {
includeProducts?: boolean UseRemoveItem,
} } from '@vercel/commerce/wishlist/use-remove-item'
import useCustomer from '../customer/use-customer'
import { useCallback } from 'react'
import { MutationHook } from '@vercel/commerce/utils/types'
import useWishlist from './use-wishlist'
import { CommerceError } from '@vercel/commerce/utils/errors'
export default useRemoveItem as UseRemoveItem<typeof handler>
export function emptyHook(options?: Options) { export const handler: MutationHook<any> = {
const useEmptyHook = async ({ id }: { id: string | number }) => { fetchOptions: {
query: '',
},
async fetcher({ input }) {
const { id } = input
let wishlist = [] let wishlist = []
const localWishlist = localStorage.getItem('wishlist') const localWishlist = localStorage.getItem('wishlist')
if (localWishlist) { if (localWishlist) {
wishlist = JSON.parse(localWishlist) wishlist = JSON.parse(localWishlist)
wishlist = wishlist.filter((p: string) => p !== id) wishlist = wishlist.filter((_p: string, key: number) => key !== id)
} }
localStorage.setItem('wishlist', JSON.stringify(wishlist)) localStorage.setItem('wishlist', JSON.stringify(wishlist))
return wishlist return wishlist
},
useHook:
({ fetch }) =>
() => {
const { mutate } = useWishlist()
const { data: customer } = useCustomer()
return useCallback(
async function addItem(input) {
if (!customer) {
// A signed customer is required in order to have a wishlist
throw new CommerceError({
message: 'Signed customer not found',
})
} }
const data = await fetch({ input })
return useEmptyHook await mutate()
return data
},
[fetch]
)
},
} }
export default emptyHook

View File

@ -1,46 +1,24 @@
import { HookFetcher } from '@vercel/commerce/utils/types' import { SWRHook } from '@vercel/commerce/utils/types'
import type { Product } from '@vercel/commerce/types/product' import useWishlist, {
import data from '../data.json' UseWishlist,
import { useCustomer } from '../customer' } from '@vercel/commerce/wishlist/use-wishlist'
import useCustomer from '../customer/use-customer'
import getContentData from '../api/utils/getContentData'
export default useWishlist as UseWishlist<typeof handler>
const defaultOpts = {} export const handler: SWRHook<any> = {
fetchOptions: {
export type Wishlist = { query: '',
items: [ },
{ async fetcher({ input }) {
variant_id: number const { customerEmail } = input
product_id: number
id: number
product: Product
}
]
}
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 = {}) => {
const { data: customer } = useCustomer()
const getWishlist = const getWishlist =
typeof localStorage !== 'undefined' && localStorage.getItem('wishlist') typeof localStorage !== 'undefined' && localStorage.getItem('wishlist')
if (getWishlist && customer?.email && data.products.length > 0) { const products = await getContentData()
if (getWishlist && customerEmail && products.length > 0) {
const wishlist = JSON.parse(getWishlist) const wishlist = JSON.parse(getWishlist)
const items = wishlist.map((wishlist: string, id: number) => { const items = wishlist.map((wishlist: string, id: number) => {
const [product] = data.products.filter((p) => const [product] = products.filter((p) =>
wishlist.startsWith(p.id) wishlist.startsWith(p.id)
) as any ) as any
const [variant] = product?.variants const [variant] = product?.variants
@ -51,14 +29,20 @@ export function extendHook(
product, product,
} }
}) })
return { data: { items } } return { items }
} }
return { data: null } return { items: [] }
},
useHook:
({ useData }) =>
(input = {}) => {
const { data: customer } = useCustomer()
return useData({
input: [['customerEmail', customer?.email]],
swrOptions: {
revalidateOnFocus: true,
...input.swrOptions,
},
})
},
} }
useWishlist.extend = extendHook
return useWishlist
}
export default extendHook(fetcher)