mirror of
https://github.com/vercel/commerce.git
synced 2025-07-24 10:41:23 +00:00
Vendure provider (#223)
* Minimal list/detail views working with Vendure * Implement useCart/useAddItem * Implement useUpdateItem & useRemoveItem * Implement useSearch * Add operations codegen, tidy up * Dummy checkout page * Implement auth/customer hooks * Use env var for Shop API url * Add some documentation * Improve error handling * Optimize preview image size * Fix accidental change * Update Vendure provider to latest changes * Vendure provider: split out gql operations, remove unused files * Update Vendure provider readme * Add local next.config to Vendure provider, update docs * Update to use demo server * Fix build errors * Use proxy for vendure api * Simplify instructions in Vendure readme * Refactor Vendure checkout api handler * Improve image quality
This commit is contained in:
5
framework/vendure/cart/index.ts
Normal file
5
framework/vendure/cart/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { default as useCart } from './use-cart'
|
||||
export { default as useAddItem } from './use-add-item'
|
||||
export { default as useRemoveItem } from './use-remove-item'
|
||||
export { default as useWishlistActions } from './use-cart-actions'
|
||||
export { default as useUpdateItem } from './use-cart-actions'
|
52
framework/vendure/cart/use-add-item.tsx
Normal file
52
framework/vendure/cart/use-add-item.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Cart, CartItemBody } from '@commerce/types'
|
||||
import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import { MutationHook } from '@commerce/utils/types'
|
||||
import { useCallback } from 'react'
|
||||
import useCart from './use-cart'
|
||||
import { AddItemToOrderMutation } from '../schema'
|
||||
import { normalizeCart } from '../lib/normalize'
|
||||
import { addItemToOrderMutation } from '../lib/mutations/add-item-to-order-mutation'
|
||||
|
||||
export default useAddItem as UseAddItem<typeof handler>
|
||||
|
||||
export const handler: MutationHook<Cart, {}, CartItemBody> = {
|
||||
fetchOptions: {
|
||||
query: addItemToOrderMutation,
|
||||
},
|
||||
async fetcher({ input, options, fetch }) {
|
||||
if (
|
||||
input.quantity &&
|
||||
(!Number.isInteger(input.quantity) || input.quantity! < 1)
|
||||
) {
|
||||
throw new CommerceError({
|
||||
message: 'The item quantity has to be a valid integer greater than 0',
|
||||
})
|
||||
}
|
||||
|
||||
const { addItemToOrder } = await fetch<AddItemToOrderMutation>({
|
||||
...options,
|
||||
variables: {
|
||||
quantity: input.quantity || 1,
|
||||
variantId: input.variantId,
|
||||
},
|
||||
})
|
||||
|
||||
if (addItemToOrder.__typename === 'Order') {
|
||||
return normalizeCart(addItemToOrder)
|
||||
}
|
||||
throw new CommerceError(addItemToOrder)
|
||||
},
|
||||
useHook: ({ fetch }) => () => {
|
||||
const { mutate } = useCart()
|
||||
|
||||
return useCallback(
|
||||
async function addItem(input) {
|
||||
const data = await fetch({ input })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
},
|
||||
[fetch, mutate]
|
||||
)
|
||||
},
|
||||
}
|
13
framework/vendure/cart/use-cart-actions.tsx
Normal file
13
framework/vendure/cart/use-cart-actions.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import useAddItem from './use-add-item'
|
||||
import useRemoveItem from './use-remove-item'
|
||||
import useUpdateItem from './use-update-item'
|
||||
|
||||
// This hook is probably not going to be used, but it's here
|
||||
// to show how a commerce should be structuring it
|
||||
export default function useCartActions() {
|
||||
const addItem = useAddItem()
|
||||
const updateItem = useUpdateItem()
|
||||
const removeItem = useRemoveItem()
|
||||
|
||||
return { addItem, updateItem, removeItem }
|
||||
}
|
49
framework/vendure/cart/use-cart.tsx
Normal file
49
framework/vendure/cart/use-cart.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Cart } from '@commerce/types'
|
||||
import { SWRHook } from '@commerce/utils/types'
|
||||
import useCart, { FetchCartInput, UseCart } from '@commerce/cart/use-cart'
|
||||
import { ActiveOrderQuery, CartFragment } from '../schema'
|
||||
import { normalizeCart } from '../lib/normalize'
|
||||
import { useMemo } from 'react'
|
||||
import { getCartQuery } from '../lib/queries/get-cart-query'
|
||||
|
||||
export type CartResult = {
|
||||
activeOrder?: CartFragment
|
||||
addItemToOrder?: CartFragment
|
||||
adjustOrderLine?: CartFragment
|
||||
removeOrderLine?: CartFragment
|
||||
}
|
||||
|
||||
export default useCart as UseCart<typeof handler>
|
||||
|
||||
export const handler: SWRHook<
|
||||
Cart | null,
|
||||
{},
|
||||
FetchCartInput,
|
||||
{ isEmpty?: boolean }
|
||||
> = {
|
||||
fetchOptions: {
|
||||
query: getCartQuery,
|
||||
},
|
||||
async fetcher({ input: { cartId }, options, fetch }) {
|
||||
const { activeOrder } = await fetch<ActiveOrderQuery>(options)
|
||||
return activeOrder ? normalizeCart(activeOrder) : null
|
||||
},
|
||||
useHook: ({ useData }) => (input) => {
|
||||
const response = useData({
|
||||
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
|
||||
})
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
Object.create(response, {
|
||||
isEmpty: {
|
||||
get() {
|
||||
return (response.data?.lineItems.length ?? 0) <= 0
|
||||
},
|
||||
enumerable: true,
|
||||
},
|
||||
}),
|
||||
[response]
|
||||
)
|
||||
},
|
||||
}
|
48
framework/vendure/cart/use-remove-item.tsx
Normal file
48
framework/vendure/cart/use-remove-item.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useCallback } from 'react'
|
||||
import { HookFetcherContext, MutationHookContext } from '@commerce/utils/types'
|
||||
import useRemoveItem, { UseRemoveItem } from '@commerce/cart/use-remove-item'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import useCart from './use-cart'
|
||||
import {
|
||||
RemoveOrderLineMutation,
|
||||
RemoveOrderLineMutationVariables,
|
||||
} from '../schema'
|
||||
import { Cart, LineItem, RemoveCartItemBody } from '@commerce/types'
|
||||
import { normalizeCart } from '../lib/normalize'
|
||||
import { removeOrderLineMutation } from '../lib/mutations/remove-order-line-mutation'
|
||||
|
||||
export default useRemoveItem as UseRemoveItem<typeof handler>
|
||||
|
||||
export const handler = {
|
||||
fetchOptions: {
|
||||
query: removeOrderLineMutation,
|
||||
},
|
||||
async fetcher({ input, options, fetch }: HookFetcherContext<LineItem>) {
|
||||
const variables: RemoveOrderLineMutationVariables = {
|
||||
orderLineId: input.id,
|
||||
}
|
||||
const { removeOrderLine } = await fetch<RemoveOrderLineMutation>({
|
||||
...options,
|
||||
variables,
|
||||
})
|
||||
|
||||
if (removeOrderLine.__typename === 'Order') {
|
||||
return normalizeCart(removeOrderLine)
|
||||
}
|
||||
throw new CommerceError(removeOrderLine)
|
||||
},
|
||||
useHook: ({
|
||||
fetch,
|
||||
}: MutationHookContext<Cart | null, RemoveCartItemBody>) => (ctx = {}) => {
|
||||
const { mutate } = useCart()
|
||||
|
||||
return useCallback(
|
||||
async function removeItem(input) {
|
||||
const data = await fetch({ input })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
},
|
||||
[fetch, mutate]
|
||||
)
|
||||
},
|
||||
}
|
78
framework/vendure/cart/use-update-item.tsx
Normal file
78
framework/vendure/cart/use-update-item.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import { useCallback } from 'react'
|
||||
import { HookFetcherContext, MutationHookContext } from '@commerce/utils/types'
|
||||
import { CommerceError, ValidationError } from '@commerce/utils/errors'
|
||||
import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item'
|
||||
import {
|
||||
Cart,
|
||||
CartItemBody,
|
||||
LineItem,
|
||||
UpdateCartItemBody,
|
||||
} from '@commerce/types'
|
||||
import useCart from './use-cart'
|
||||
import {
|
||||
AdjustOrderLineMutation,
|
||||
AdjustOrderLineMutationVariables,
|
||||
} from '../schema'
|
||||
import { normalizeCart } from '../lib/normalize'
|
||||
import { adjustOrderLineMutation } from '../lib/mutations/adjust-order-line-mutation'
|
||||
|
||||
export default useUpdateItem as UseUpdateItem<typeof handler>
|
||||
|
||||
export const handler = {
|
||||
fetchOptions: {
|
||||
query: adjustOrderLineMutation,
|
||||
},
|
||||
async fetcher(context: HookFetcherContext<UpdateCartItemBody<CartItemBody>>) {
|
||||
const { input, options, fetch } = context
|
||||
const variables: AdjustOrderLineMutationVariables = {
|
||||
quantity: input.item.quantity || 1,
|
||||
orderLineId: input.itemId,
|
||||
}
|
||||
const { adjustOrderLine } = await fetch<AdjustOrderLineMutation>({
|
||||
...options,
|
||||
variables,
|
||||
})
|
||||
|
||||
if (adjustOrderLine.__typename === 'Order') {
|
||||
return normalizeCart(adjustOrderLine)
|
||||
}
|
||||
throw new CommerceError(adjustOrderLine)
|
||||
},
|
||||
useHook: ({
|
||||
fetch,
|
||||
}: MutationHookContext<Cart | null, UpdateCartItemBody<CartItemBody>>) => (
|
||||
ctx: {
|
||||
item?: LineItem
|
||||
wait?: number
|
||||
} = {}
|
||||
) => {
|
||||
const { item } = ctx
|
||||
const { mutate } = useCart()
|
||||
|
||||
return useCallback(
|
||||
async function addItem(input: Partial<CartItemBody>) {
|
||||
const itemId = item?.id
|
||||
const productId = input.productId ?? item?.productId
|
||||
const variantId = input.productId ?? item?.variantId
|
||||
if (!itemId || !productId || !variantId) {
|
||||
throw new ValidationError({
|
||||
message: 'Invalid input used for this operation',
|
||||
})
|
||||
}
|
||||
const data = await fetch({
|
||||
input: {
|
||||
item: {
|
||||
productId,
|
||||
variantId,
|
||||
quantity: input.quantity,
|
||||
},
|
||||
itemId,
|
||||
},
|
||||
})
|
||||
await mutate(data, false)
|
||||
return data
|
||||
},
|
||||
[fetch, mutate]
|
||||
)
|
||||
},
|
||||
}
|
Reference in New Issue
Block a user