mirror of
https://github.com/vercel/commerce.git
synced 2025-06-18 13:11:23 +00:00
Implement useUpdateItem & useRemoveItem
This commit is contained in:
parent
0f7c151805
commit
d0ed97466c
27
framework/vendure/api/fragments/cart.ts
Normal file
27
framework/vendure/api/fragments/cart.ts
Normal file
@ -0,0 +1,27 @@
|
||||
export const cartFragment = /* GraphQL */ `
|
||||
fragment Cart on Order {
|
||||
id
|
||||
code
|
||||
totalQuantity
|
||||
subTotal
|
||||
subTotalWithTax
|
||||
total
|
||||
totalWithTax
|
||||
currencyCode
|
||||
lines {
|
||||
id
|
||||
quantity
|
||||
featuredAsset {
|
||||
id
|
||||
preview
|
||||
}
|
||||
productVariant {
|
||||
name
|
||||
product {
|
||||
slug
|
||||
}
|
||||
productId
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
@ -1,9 +0,0 @@
|
||||
export const categoryTreeItemFragment = /* GraphQL */ `
|
||||
fragment categoryTreeItem on CategoryTreeItem {
|
||||
entityId
|
||||
name
|
||||
path
|
||||
description
|
||||
productCount
|
||||
}
|
||||
`
|
@ -1,113 +0,0 @@
|
||||
export const productPrices = /* GraphQL */ `
|
||||
fragment productPrices on Prices {
|
||||
price {
|
||||
value
|
||||
currencyCode
|
||||
}
|
||||
salePrice {
|
||||
value
|
||||
currencyCode
|
||||
}
|
||||
retailPrice {
|
||||
value
|
||||
currencyCode
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const swatchOptionFragment = /* GraphQL */ `
|
||||
fragment swatchOption on SwatchOptionValue {
|
||||
isDefault
|
||||
hexColors
|
||||
}
|
||||
`
|
||||
|
||||
export const multipleChoiceOptionFragment = /* GraphQL */ `
|
||||
fragment multipleChoiceOption on MultipleChoiceOption {
|
||||
values {
|
||||
edges {
|
||||
node {
|
||||
label
|
||||
...swatchOption
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${swatchOptionFragment}
|
||||
`
|
||||
|
||||
export const productInfoFragment = /* GraphQL */ `
|
||||
fragment productInfo on Product {
|
||||
entityId
|
||||
name
|
||||
path
|
||||
brand {
|
||||
entityId
|
||||
}
|
||||
description
|
||||
prices {
|
||||
...productPrices
|
||||
}
|
||||
images {
|
||||
edges {
|
||||
node {
|
||||
urlOriginal
|
||||
altText
|
||||
isDefault
|
||||
}
|
||||
}
|
||||
}
|
||||
variants {
|
||||
edges {
|
||||
node {
|
||||
entityId
|
||||
defaultImage {
|
||||
urlOriginal
|
||||
altText
|
||||
isDefault
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
productOptions {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
entityId
|
||||
displayName
|
||||
...multipleChoiceOption
|
||||
}
|
||||
}
|
||||
}
|
||||
localeMeta: metafields(namespace: $locale, keys: ["name", "description"])
|
||||
@include(if: $hasLocale) {
|
||||
edges {
|
||||
node {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${productPrices}
|
||||
${multipleChoiceOptionFragment}
|
||||
`
|
||||
|
||||
export const productConnectionFragment = /* GraphQL */ `
|
||||
fragment productConnnection on ProductConnection {
|
||||
pageInfo {
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
...productInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${productInfoFragment}
|
||||
`
|
@ -4,28 +4,15 @@ import fetchGraphqlApi from '@framework/api/utils/fetch-graphql-api'
|
||||
import useCartAddItem from '@commerce/cart/use-add-item'
|
||||
import useCart from './use-cart'
|
||||
import { useCallback } from 'react'
|
||||
import { cartFragment } from '../api/fragments/cart'
|
||||
|
||||
export const addItemToOrderMutation = /* GraphQL */ `
|
||||
mutation addItemToOrder($variantId: ID!, $quantity: Int!) {
|
||||
addItemToOrder(productVariantId: $variantId, quantity: $quantity) {
|
||||
... on Order {
|
||||
id
|
||||
code
|
||||
totalQuantity
|
||||
total
|
||||
totalWithTax
|
||||
lines {
|
||||
id
|
||||
productVariant {
|
||||
featuredAsset {
|
||||
id
|
||||
preview
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... Cart
|
||||
}
|
||||
}
|
||||
${cartFragment}
|
||||
`
|
||||
|
||||
export type AddItemInput = { productId?: number; variantId: number; quantity?: number; };
|
||||
|
@ -6,35 +6,15 @@ import useResponse from '@commerce/utils/use-response'
|
||||
import useAction from '@commerce/utils/use-action'
|
||||
import { useCallback } from 'react'
|
||||
import { normalizeCart } from '../../bigcommerce/lib/normalize'
|
||||
import { cartFragment } from '../api/fragments/cart'
|
||||
|
||||
export const getCartQuery = /* GraphQL */ `
|
||||
query activeOrder {
|
||||
activeOrder {
|
||||
id
|
||||
code
|
||||
totalQuantity
|
||||
subTotal
|
||||
subTotalWithTax
|
||||
total
|
||||
totalWithTax
|
||||
currencyCode
|
||||
lines {
|
||||
id
|
||||
quantity
|
||||
featuredAsset {
|
||||
id
|
||||
preview
|
||||
}
|
||||
productVariant {
|
||||
name
|
||||
product {
|
||||
slug
|
||||
}
|
||||
productId
|
||||
}
|
||||
}
|
||||
...Cart
|
||||
}
|
||||
}
|
||||
${cartFragment}
|
||||
`
|
||||
|
||||
export const fetcher: HookFetcher<any | null> = (
|
||||
@ -50,17 +30,17 @@ export function extendHook(
|
||||
swrOptions?: SwrOptions<any | null>
|
||||
) {
|
||||
const useCart = () => {
|
||||
const response = useData({}, [], customFetcher, swrOptions)
|
||||
const response = useData({ query: getCartQuery }, [], customFetcher, swrOptions)
|
||||
const res = useResponse(response, {
|
||||
normalizer: (data => {
|
||||
const order = data?.activeOrder;
|
||||
console.log({ order });
|
||||
const order = data?.activeOrder || data?.addItemToOrder || data?.adjustOrderLine || data?.removeOrderLine;
|
||||
return (order ? {
|
||||
id: order.id,
|
||||
currency: { code: order.currencyCode },
|
||||
subTotal: order.subTotalWithTax / 100,
|
||||
total: order.totalWithTax / 100,
|
||||
items: order.lines?.map(l => ({
|
||||
id: l.id,
|
||||
name: l.productVariant.name,
|
||||
quantity: l.quantity,
|
||||
url: l.productVariant.product.slug,
|
||||
|
@ -1,41 +1,41 @@
|
||||
import { useCallback } from 'react'
|
||||
import { HookFetcher } from '@commerce/utils/types'
|
||||
import useCartRemoveItem from '@commerce/cart/use-remove-item'
|
||||
import type { RemoveItemBody } from '../api/cart'
|
||||
import useCart, { Cart } from './use-cart'
|
||||
import { cartFragment } from '@framework/api/fragments/cart'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/cart',
|
||||
method: 'DELETE',
|
||||
}
|
||||
export const removeOrderLineMutation = /* GraphQL */ `
|
||||
mutation removeOrderLine($orderLineId: ID!) {
|
||||
removeOrderLine(orderLineId: $orderLineId) {
|
||||
...Cart
|
||||
}
|
||||
}
|
||||
${cartFragment}
|
||||
`
|
||||
|
||||
export type RemoveItemInput = {
|
||||
id: string
|
||||
}
|
||||
|
||||
export const fetcher: HookFetcher<Cart | null, RemoveItemBody> = (
|
||||
export const fetcher: HookFetcher<Cart | null, any> = (
|
||||
options,
|
||||
{ itemId },
|
||||
{ lineId },
|
||||
fetch
|
||||
) => {
|
||||
return fetch({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
body: { itemId },
|
||||
query: removeOrderLineMutation,
|
||||
variables: { orderLineId: lineId },
|
||||
})
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useRemoveItem = (item?: any) => {
|
||||
const { mutate } = useCart()
|
||||
const fn = useCartRemoveItem<Cart | null, RemoveItemBody>(
|
||||
defaultOpts,
|
||||
const fn = useCartRemoveItem<Cart | null, any>(
|
||||
{},
|
||||
customFetcher
|
||||
)
|
||||
|
||||
return useCallback(
|
||||
async function removeItem(input: RemoveItemInput) {
|
||||
const data = await fn({ itemId: input.id ?? item?.id })
|
||||
async function removeItem(input: any) {
|
||||
const data = await fn({ lineId: input.id })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
},
|
||||
|
@ -1,59 +1,43 @@
|
||||
import { useCallback } from 'react'
|
||||
import debounce from 'lodash.debounce'
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import useCartUpdateItem from '@commerce/cart/use-update-item'
|
||||
import type { ItemBody, UpdateItemBody } from '../api/cart'
|
||||
import { fetcher as removeFetcher } from './use-remove-item'
|
||||
import useCart, { Cart } from './use-cart'
|
||||
import { cartFragment } from '@framework/api/fragments/cart'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/cart',
|
||||
method: 'PUT',
|
||||
}
|
||||
|
||||
export type UpdateItemInput = Partial<{ id: string } & ItemBody>
|
||||
|
||||
export const fetcher: HookFetcher<Cart | null, UpdateItemBody> = (
|
||||
export const adjustOrderLineMutation = /* GraphQL */ `
|
||||
mutation adjustOrderLine($orderLineId: ID!, $quantity: Int!) {
|
||||
adjustOrderLine(orderLineId: $orderLineId, quantity: $quantity) {
|
||||
...Cart
|
||||
}
|
||||
}
|
||||
${cartFragment}
|
||||
`
|
||||
export const fetcher: HookFetcher<Cart | null, any> = (
|
||||
options,
|
||||
{ itemId, item },
|
||||
{ lineId, quantity },
|
||||
fetch
|
||||
) => {
|
||||
if (Number.isInteger(item.quantity)) {
|
||||
// Also allow the update hook to remove an item if the quantity is lower than 1
|
||||
if (item.quantity! < 1) {
|
||||
return removeFetcher(null, { itemId }, fetch)
|
||||
}
|
||||
} else if (item.quantity) {
|
||||
throw new CommerceError({
|
||||
message: 'The item quantity has to be a valid integer',
|
||||
})
|
||||
}
|
||||
|
||||
return fetch({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
body: { itemId, item },
|
||||
query: adjustOrderLineMutation,
|
||||
variables: { orderLineId: lineId, quantity },
|
||||
})
|
||||
}
|
||||
|
||||
function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) {
|
||||
const useUpdateItem = (item?: any) => {
|
||||
const { mutate } = useCart()
|
||||
const fn = useCartUpdateItem<Cart | null, UpdateItemBody>(
|
||||
defaultOpts,
|
||||
const fn = useCartUpdateItem<Cart | null, any>(
|
||||
{},
|
||||
customFetcher
|
||||
)
|
||||
|
||||
return useCallback(
|
||||
debounce(async (input: UpdateItemInput) => {
|
||||
debounce(async (input: any) => {
|
||||
const data = await fn({
|
||||
itemId: input.id ?? item?.id,
|
||||
item: {
|
||||
productId: input.productId ?? item?.product_id,
|
||||
variantId: input.productId ?? item?.variant_id,
|
||||
quantity: input.quantity,
|
||||
},
|
||||
lineId: item.id,
|
||||
quantity: input.quantity,
|
||||
})
|
||||
await mutate(data, false)
|
||||
return data
|
||||
|
Loading…
x
Reference in New Issue
Block a user