From b3911fe0d11763d2891722154903154af6132b70 Mon Sep 17 00:00:00 2001 From: Alessandro Casazza Date: Tue, 17 Aug 2021 18:18:39 +0200 Subject: [PATCH] feat: Add update and remove item --- components/product/helpers.ts | 21 ++++++- framework/commercelayer/api/utils/cookies.ts | 2 + .../commercelayer/api/utils/getCredentials.ts | 7 +++ .../api/utils/normalizeLineItems.tsx | 26 +++++++++ framework/commercelayer/cart/use-add-item.tsx | 58 +++++++++++++++++-- framework/commercelayer/cart/use-cart.tsx | 46 +++++++++++---- .../commercelayer/cart/use-remove-item.tsx | 13 ++++- .../commercelayer/cart/use-update-item.tsx | 44 ++++++++++++-- framework/commercelayer/commerce.config.json | 2 +- 9 files changed, 195 insertions(+), 24 deletions(-) create mode 100644 framework/commercelayer/api/utils/getCredentials.ts create mode 100644 framework/commercelayer/api/utils/normalizeLineItems.tsx diff --git a/components/product/helpers.ts b/components/product/helpers.ts index 77e385bb8..7fff369a1 100644 --- a/components/product/helpers.ts +++ b/components/product/helpers.ts @@ -1,4 +1,4 @@ -import type { Product } from '@commerce/types/product' +import type { Product, ProductVariant } from '@commerce/types/product' export type SelectedOptions = Record import { Dispatch, SetStateAction } from 'react' @@ -30,3 +30,22 @@ export function selectDefaultOptionFromProduct( })) }) } + +export function getSelectedOptionsIds( + variant: ProductVariant, + opts: SelectedOptions +) { + const [selected] = variant.options.filter((option) => { + const type = option.displayName.toLowerCase() + const typeSelected = option.values.filter( + ({ label }) => label.toLowerCase() === opts[type] + ) + if (typeSelected.length > 0) return true + return false + }) + return selected + ? { + [`${selected.displayName?.toLowerCase()}Id`]: selected.id, + } + : {} +} diff --git a/framework/commercelayer/api/utils/cookies.ts b/framework/commercelayer/api/utils/cookies.ts index d17c1bfff..2427e0f98 100644 --- a/framework/commercelayer/api/utils/cookies.ts +++ b/framework/commercelayer/api/utils/cookies.ts @@ -1,5 +1,7 @@ import Cookies, { CookieAttributes } from 'js-cookie' +export const getCookie = (name: string): string | undefined => Cookies.get(name) + const setCookie = ( name: string, token?: string, diff --git a/framework/commercelayer/api/utils/getCredentials.ts b/framework/commercelayer/api/utils/getCredentials.ts new file mode 100644 index 000000000..8ee0a3bf7 --- /dev/null +++ b/framework/commercelayer/api/utils/getCredentials.ts @@ -0,0 +1,7 @@ +import { getCookie } from './cookies' + +export default function getCredentials() { + const endpoint = process.env.NEXT_PUBLIC_COMMERCELAYER_ENDPOINT as string + const accessToken = getCookie('CL_TOKEN') as string + return { accessToken, endpoint } +} diff --git a/framework/commercelayer/api/utils/normalizeLineItems.tsx b/framework/commercelayer/api/utils/normalizeLineItems.tsx new file mode 100644 index 000000000..b0bd99c56 --- /dev/null +++ b/framework/commercelayer/api/utils/normalizeLineItems.tsx @@ -0,0 +1,26 @@ +export default function normalizeLineItems(lineItems: any[]) { + return lineItems.map((lineItem) => { + const id = lineItem.id + const attributes = lineItem.attributes + return { + id, + name: attributes.name, + productId: attributes.reference, + variantId: attributes.reference, + quantity: attributes.quantity, + price: attributes.unit_amount_float, + variant: { + id, + name: attributes.name, + sku: attributes.sku_code, + price: attributes.unit_amount_float, + image: { + url: `/commercelayer_assets/${attributes.reference}_FLAT.png`, + altText: 'Black Women Long Sleeve Shirt', + width: 1000, + height: 1000, + }, + }, + } + }) +} diff --git a/framework/commercelayer/cart/use-add-item.tsx b/framework/commercelayer/cart/use-add-item.tsx index 7f3d1061f..51b5eaf7d 100644 --- a/framework/commercelayer/cart/use-add-item.tsx +++ b/framework/commercelayer/cart/use-add-item.tsx @@ -1,17 +1,67 @@ import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' import { MutationHook } from '@commerce/utils/types' +import { LineItem, Order } from '@commercelayer/js-sdk' +import setCookie, { getCookie } from '@framework/api/utils/cookies' +import getCredentials from '@framework/api/utils/getCredentials' +import useCart from '@framework/cart/use-cart' +import { useCallback } from 'react' export default useAddItem as UseAddItem export const handler: MutationHook = { fetchOptions: { query: '', }, - async fetcher({ input, options, fetch }) {}, + async fetcher({ input, options, fetch }) { + const localOrderId = localStorage.getItem('CL_ORDER') + const credentials = getCredentials() + const orderId = + localOrderId || + (credentials.accessToken && + (await Order.withCredentials(credentials).create({})).id) + if (orderId && input.sizeId) { + !localOrderId && localStorage.setItem('CL_ORDER', orderId) + const lineItem = await LineItem.withCredentials(credentials).create( + { + skuCode: input.sizeId, + order: Order.build({ id: orderId }), + quantity: 1, + reference: input.variantId, + _update_quantity: 1, + }, + // @ts-ignore + { rawResponse: true } + ) + const attributes = lineItem.data.attributes + return { + id: lineItem.data.id, + name: attributes.name, + productId: input.productId, + variantId: input.variantId, + quantity: attributes.quantity, + price: attributes.unit_amount_float, + variant: { + id: lineItem.data.id, + name: attributes.name, + sku: input.sizeId, + price: attributes.unit_amount_float, + image: { + url: `/commercelayer_assets/${input.variantId}_FLAT.png`, + altText: 'Black Women Long Sleeve Shirt', + width: 1000, + height: 1000, + }, + }, + } + } + }, useHook: ({ fetch }) => () => { - return async function addItem() { - return {} - } + return useCallback( + async function addItem(input) { + return await fetch({ input }) + }, + [fetch] + ) }, } diff --git a/framework/commercelayer/cart/use-cart.tsx b/framework/commercelayer/cart/use-cart.tsx index b3e509a21..a1e7dc3ee 100644 --- a/framework/commercelayer/cart/use-cart.tsx +++ b/framework/commercelayer/cart/use-cart.tsx @@ -1,6 +1,9 @@ import { useMemo } from 'react' import { SWRHook } from '@commerce/utils/types' import useCart, { UseCart } from '@commerce/cart/use-cart' +import { Order } from '@commercelayer/js-sdk' +import getCredentials from '@framework/api/utils/getCredentials' +import normalizeLineItems from '../api/utils/normalizeLineItems' export default useCart as UseCart @@ -9,6 +12,27 @@ export const handler: SWRHook = { query: '', }, async fetcher() { + const id = localStorage.getItem('CL_ORDER') || '' + const credentials = getCredentials() + if (id && credentials.accessToken) { + const clOrder = await Order.withCredentials(credentials) + .includes('lineItems') + .find(id, { rawResponse: true }) + const attributes = clOrder.data.attributes + const lineItems = clOrder?.included + ? normalizeLineItems(clOrder?.included) + : [] + return { + id, + createdAt: attributes.created_at, + currency: { code: attributes.currency_code }, + taxesIncluded: '', + lineItems, + lineItemsSubtotalPrice: '', + subtotalPrice: attributes.subtotal_amount_float, + totalPrice: attributes.total_amount_float, + } + } return { id: '', createdAt: '', @@ -22,21 +46,19 @@ export const handler: SWRHook = { }, useHook: ({ useData }) => - (input) => { + () => { + const response = useData() return useMemo( () => - Object.create( - {}, - { - isEmpty: { - get() { - return true - }, - enumerable: true, + Object.create(response, { + isEmpty: { + get() { + return response.data.lineItems.length === 0 }, - } - ), - [] + enumerable: true, + }, + }), + [response] ) }, } diff --git a/framework/commercelayer/cart/use-remove-item.tsx b/framework/commercelayer/cart/use-remove-item.tsx index b4ed583b8..c4ade3973 100644 --- a/framework/commercelayer/cart/use-remove-item.tsx +++ b/framework/commercelayer/cart/use-remove-item.tsx @@ -1,5 +1,7 @@ import { MutationHook } from '@commerce/utils/types' import useRemoveItem, { UseRemoveItem } from '@commerce/cart/use-remove-item' +import getCredentials from '@framework/api/utils/getCredentials' +import { LineItem } from '@commercelayer/js-sdk' export default useRemoveItem as UseRemoveItem @@ -7,12 +9,19 @@ export const handler: MutationHook = { fetchOptions: { query: '', }, - async fetcher({ input, options, fetch }) {}, + async fetcher({ input: { id } }) { + const credentials = getCredentials() + const orderId = localStorage.getItem('CL_ORDER') + if (orderId && id) { + await LineItem.build({ id }).withCredentials(credentials).destroy() + return {} + } + }, useHook: ({ fetch }) => () => { return async function removeItem(input) { - return {} + return await fetch({ input }) } }, } diff --git a/framework/commercelayer/cart/use-update-item.tsx b/framework/commercelayer/cart/use-update-item.tsx index 06d703f70..4d2cfcf49 100644 --- a/framework/commercelayer/cart/use-update-item.tsx +++ b/framework/commercelayer/cart/use-update-item.tsx @@ -1,5 +1,9 @@ import { MutationHook } from '@commerce/utils/types' import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item' +import useCart from '@commerce/cart/use-cart' +import { useCallback } from 'react' +import getCredentials from '@framework/api/utils/getCredentials' +import { LineItem } from '@commercelayer/js-sdk' export default useUpdateItem as UseUpdateItem @@ -7,12 +11,44 @@ export const handler: MutationHook = { fetchOptions: { query: '', }, - async fetcher({ input, options, fetch }) {}, + async fetcher({ input: { item, quantity } }) { + const credentials = getCredentials() + const orderId = localStorage.getItem('CL_ORDER') + if (orderId && item.id) { + const lineItem = (await LineItem.build({ + id: item.id, + }) + .withCredentials(credentials) + // @ts-ignore + .update({ quantity }, null, { rawResponse: true })) as any + const attributes = lineItem.data.attributes + return { + id: lineItem.data.id, + name: attributes.name, + productId: item.productId, + variantId: item.variantId, + quantity: attributes.quantity, + price: attributes.unit_amount_float, + variant: { + id: lineItem.data.id, + name: attributes.name, + sku: lineItem.data.sku_code, + price: attributes.unit_amount_float, + image: { + url: `/commercelayer_assets/${item.variantId}_FLAT.png`, + altText: 'Black Women Long Sleeve Shirt', + width: 1000, + height: 1000, + }, + }, + } + } + }, useHook: ({ fetch }) => - () => { - return async function addItem() { - return {} + ({ item }) => { + return async function updateItem(input) { + return await fetch({ input: { item, ...input } }) } }, } diff --git a/framework/commercelayer/commerce.config.json b/framework/commercelayer/commerce.config.json index 460e52b3a..3cecf7921 100644 --- a/framework/commercelayer/commerce.config.json +++ b/framework/commercelayer/commerce.config.json @@ -2,7 +2,7 @@ "provider": "commercelayer", "features": { "wishlist": false, - "cart": false, + "cart": true, "search": false, "customerAuth": true }