feat: add medusa-react (#913)
This commit is contained in:
98
packages/medusa-react/src/contexts/cart.tsx
Normal file
98
packages/medusa-react/src/contexts/cart.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import React, { useState } from "react"
|
||||
import {
|
||||
useAddShippingMethodToCart,
|
||||
useCompleteCart,
|
||||
useCreateCart,
|
||||
useSetPaymentSession,
|
||||
useUpdateCart,
|
||||
useCreatePaymentSession,
|
||||
} from "../hooks/carts"
|
||||
import { Cart } from "../types"
|
||||
|
||||
interface CartState {
|
||||
cart?: Cart
|
||||
}
|
||||
|
||||
interface CartContext extends CartState {
|
||||
setCart: (cart: Cart) => void
|
||||
pay: ReturnType<typeof useSetPaymentSession>
|
||||
createCart: ReturnType<typeof useCreateCart>
|
||||
startCheckout: ReturnType<typeof useCreatePaymentSession>
|
||||
completeCheckout: ReturnType<typeof useCompleteCart>
|
||||
updateCart: ReturnType<typeof useUpdateCart>
|
||||
addShippingMethod: ReturnType<typeof useAddShippingMethodToCart>
|
||||
totalItems: number
|
||||
}
|
||||
|
||||
const CartContext = React.createContext<CartContext | null>(null)
|
||||
|
||||
export const useCart = () => {
|
||||
const context = React.useContext(CartContext)
|
||||
if (!context) {
|
||||
throw new Error("useCart must be used within a CartProvider")
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
interface CartProps {
|
||||
children: React.ReactNode
|
||||
initialState?: Cart
|
||||
}
|
||||
|
||||
const defaultInitialState = {
|
||||
id: "",
|
||||
items: [] as any,
|
||||
} as Cart
|
||||
|
||||
export const CartProvider = ({
|
||||
children,
|
||||
initialState = defaultInitialState,
|
||||
}: CartProps) => {
|
||||
const [cart, setCart] = useState<Cart>(initialState)
|
||||
|
||||
const createCart = useCreateCart({
|
||||
onSuccess: ({ cart }) => setCart(cart),
|
||||
})
|
||||
|
||||
const updateCart = useUpdateCart(cart?.id, {
|
||||
onSuccess: ({ cart }) => setCart(cart),
|
||||
})
|
||||
|
||||
const addShippingMethod = useAddShippingMethodToCart(cart?.id, {
|
||||
onSuccess: ({ cart }) => setCart(cart),
|
||||
})
|
||||
|
||||
const startCheckout = useCreatePaymentSession(cart?.id, {
|
||||
onSuccess: ({ cart }) => setCart(cart),
|
||||
})
|
||||
|
||||
const pay = useSetPaymentSession(cart?.id, {
|
||||
onSuccess: ({ cart }) => {
|
||||
setCart(cart)
|
||||
},
|
||||
})
|
||||
|
||||
const completeCheckout = useCompleteCart(cart?.id)
|
||||
|
||||
const totalItems = cart?.items
|
||||
.map((i) => i.quantity)
|
||||
.reduce((acc, curr) => acc + curr, 0)
|
||||
|
||||
return (
|
||||
<CartContext.Provider
|
||||
value={{
|
||||
cart,
|
||||
setCart,
|
||||
createCart,
|
||||
pay,
|
||||
startCheckout,
|
||||
completeCheckout,
|
||||
updateCart,
|
||||
addShippingMethod,
|
||||
totalItems: totalItems || 0,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</CartContext.Provider>
|
||||
)
|
||||
}
|
||||
3
packages/medusa-react/src/contexts/index.ts
Normal file
3
packages/medusa-react/src/contexts/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./medusa"
|
||||
export * from "./session-cart"
|
||||
export * from "./cart"
|
||||
38
packages/medusa-react/src/contexts/medusa.tsx
Normal file
38
packages/medusa-react/src/contexts/medusa.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import React from "react"
|
||||
import { QueryClientProvider, QueryClientProviderProps } from "react-query"
|
||||
import Medusa from "@medusajs/medusa-js"
|
||||
|
||||
interface MedusaContextState {
|
||||
client: Medusa
|
||||
}
|
||||
|
||||
const MedusaContext = React.createContext<MedusaContextState | null>(null)
|
||||
|
||||
export const useMedusa = () => {
|
||||
const context = React.useContext(MedusaContext)
|
||||
if (!context) {
|
||||
throw new Error("useMedusa must be used within a MedusaProvider")
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
interface MedusaProviderProps {
|
||||
baseUrl: string
|
||||
queryClientProviderProps: QueryClientProviderProps
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export const MedusaProvider = ({
|
||||
queryClientProviderProps,
|
||||
baseUrl,
|
||||
children,
|
||||
}: MedusaProviderProps) => {
|
||||
const medusaClient = new Medusa({ baseUrl, maxRetries: 0 })
|
||||
return (
|
||||
<QueryClientProvider {...queryClientProviderProps}>
|
||||
<MedusaContext.Provider value={{ client: medusaClient }}>
|
||||
{children}
|
||||
</MedusaContext.Provider>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
289
packages/medusa-react/src/contexts/session-cart.tsx
Normal file
289
packages/medusa-react/src/contexts/session-cart.tsx
Normal file
@@ -0,0 +1,289 @@
|
||||
import React, { useContext, useEffect } from "react"
|
||||
import { useLocalStorage } from "../hooks/utils"
|
||||
import { RegionInfo, ProductVariant } from "../types"
|
||||
import { getVariantPrice } from "../utils"
|
||||
import { isArray, isEmpty, isObject } from "lodash"
|
||||
|
||||
interface Item {
|
||||
variant: ProductVariant
|
||||
quantity: number
|
||||
readonly total?: number
|
||||
}
|
||||
|
||||
export interface SessionCartState {
|
||||
region: RegionInfo
|
||||
items: Item[]
|
||||
totalItems: number
|
||||
total: number
|
||||
}
|
||||
|
||||
interface SessionCartContextState extends SessionCartState {
|
||||
setRegion: (region: RegionInfo) => void
|
||||
addItem: (item: Item) => void
|
||||
removeItem: (id: string) => void
|
||||
updateItem: (id: string, item: Partial<Item>) => void
|
||||
setItems: (items: Item[]) => void
|
||||
updateItemQuantity: (id: string, quantity: number) => void
|
||||
incrementItemQuantity: (id: string) => void
|
||||
decrementItemQuantity: (id: string) => void
|
||||
getItem: (id: string) => Item | undefined
|
||||
clearItems: () => void
|
||||
}
|
||||
|
||||
const SessionCartContext = React.createContext<SessionCartContextState | null>(
|
||||
null
|
||||
)
|
||||
|
||||
enum ACTION_TYPES {
|
||||
INIT,
|
||||
ADD_ITEM,
|
||||
SET_ITEMS,
|
||||
REMOVE_ITEM,
|
||||
UPDATE_ITEM,
|
||||
CLEAR_ITEMS,
|
||||
SET_REGION,
|
||||
}
|
||||
|
||||
type Action =
|
||||
| { type: ACTION_TYPES.SET_REGION; payload: RegionInfo }
|
||||
| { type: ACTION_TYPES.INIT; payload: object }
|
||||
| { type: ACTION_TYPES.ADD_ITEM; payload: Item }
|
||||
| {
|
||||
type: ACTION_TYPES.UPDATE_ITEM
|
||||
payload: { id: string; item: Partial<Item> }
|
||||
}
|
||||
| { type: ACTION_TYPES.REMOVE_ITEM; payload: { id: string } }
|
||||
| { type: ACTION_TYPES.SET_ITEMS; payload: Item[] }
|
||||
| { type: ACTION_TYPES.CLEAR_ITEMS }
|
||||
|
||||
const reducer = (state: SessionCartState, action: Action) => {
|
||||
switch (action.type) {
|
||||
case ACTION_TYPES.INIT: {
|
||||
return state
|
||||
}
|
||||
case ACTION_TYPES.SET_REGION: {
|
||||
return generateCartState(
|
||||
{
|
||||
...state,
|
||||
region: action.payload,
|
||||
},
|
||||
state.items
|
||||
)
|
||||
}
|
||||
case ACTION_TYPES.ADD_ITEM: {
|
||||
const duplicateVariantIndex = state.items.findIndex(
|
||||
(item) => item.variant.id === action.payload?.variant?.id
|
||||
)
|
||||
if (duplicateVariantIndex !== -1) {
|
||||
state.items.splice(duplicateVariantIndex, 1)
|
||||
}
|
||||
const items = [...state.items, action.payload]
|
||||
return generateCartState(state, items)
|
||||
}
|
||||
case ACTION_TYPES.UPDATE_ITEM: {
|
||||
const items = state.items.map((item) =>
|
||||
item.variant.id === action.payload.id
|
||||
? { ...item, ...action.payload.item }
|
||||
: item
|
||||
)
|
||||
|
||||
return generateCartState(state, items)
|
||||
}
|
||||
case ACTION_TYPES.REMOVE_ITEM: {
|
||||
const items = state.items.filter(
|
||||
(item) => item.variant.id !== action.payload.id
|
||||
)
|
||||
return generateCartState(state, items)
|
||||
}
|
||||
case ACTION_TYPES.SET_ITEMS: {
|
||||
return generateCartState(state, action.payload)
|
||||
}
|
||||
case ACTION_TYPES.CLEAR_ITEMS: {
|
||||
return {
|
||||
...state,
|
||||
items: [],
|
||||
total: 0,
|
||||
totalItems: 0,
|
||||
}
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
export const generateCartState = (state: SessionCartState, items: Item[]) => {
|
||||
const newItems = generateItems(state.region, items)
|
||||
return {
|
||||
...state,
|
||||
items: newItems,
|
||||
totalItems: items.reduce((sum, item) => sum + item.quantity, 0),
|
||||
total: calculateSessionCartTotal(newItems),
|
||||
}
|
||||
}
|
||||
|
||||
const generateItems = (region: RegionInfo, items: Item[]) => {
|
||||
return items.map((item) => ({
|
||||
...item,
|
||||
total: getVariantPrice(item.variant, region),
|
||||
}))
|
||||
}
|
||||
|
||||
const calculateSessionCartTotal = (items: Item[]) => {
|
||||
return items.reduce(
|
||||
(total, item) => total + item.quantity * (item.total || 0),
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
interface SessionCartProviderProps {
|
||||
children: React.ReactNode
|
||||
initialState?: SessionCartState
|
||||
}
|
||||
|
||||
const defaultInitialState: SessionCartState = {
|
||||
region: {} as RegionInfo,
|
||||
items: [],
|
||||
total: 0,
|
||||
totalItems: 0,
|
||||
}
|
||||
|
||||
export const SessionCartProvider = ({
|
||||
initialState = defaultInitialState,
|
||||
children,
|
||||
}: SessionCartProviderProps) => {
|
||||
const [saved, save] = useLocalStorage(
|
||||
"medusa-session-cart",
|
||||
JSON.stringify(initialState)
|
||||
)
|
||||
|
||||
const [state, dispatch] = React.useReducer(reducer, JSON.parse(saved))
|
||||
|
||||
useEffect(() => {
|
||||
save(JSON.stringify(state))
|
||||
}, [state, save])
|
||||
|
||||
const setRegion = (region: RegionInfo) => {
|
||||
if (!isObject(region) || isEmpty(region)) {
|
||||
throw new Error("region must be a non-empty object")
|
||||
}
|
||||
|
||||
dispatch({ type: ACTION_TYPES.SET_REGION, payload: region })
|
||||
}
|
||||
|
||||
const getItem = (id: string) => {
|
||||
return state.items.find((item) => item.variant.id === id)
|
||||
}
|
||||
|
||||
const setItems = (items: Item[]) => {
|
||||
if (!isArray(items)) {
|
||||
throw new Error("items must be an array of items")
|
||||
}
|
||||
|
||||
dispatch({ type: ACTION_TYPES.SET_ITEMS, payload: items })
|
||||
}
|
||||
|
||||
const addItem = (item: Item) => {
|
||||
if (!isObject(item) || isEmpty(item)) {
|
||||
throw new Error("item must be a non-empty object")
|
||||
}
|
||||
|
||||
dispatch({ type: ACTION_TYPES.ADD_ITEM, payload: item })
|
||||
}
|
||||
|
||||
const updateItem = (id: string, item: Partial<Item>) => {
|
||||
dispatch({ type: ACTION_TYPES.UPDATE_ITEM, payload: { id, item } })
|
||||
}
|
||||
|
||||
const updateItemQuantity = (id: string, quantity: number) => {
|
||||
const item = getItem(id)
|
||||
if (!item) return
|
||||
|
||||
quantity = quantity <= 0 ? 1 : quantity
|
||||
|
||||
dispatch({
|
||||
type: ACTION_TYPES.UPDATE_ITEM,
|
||||
payload: {
|
||||
id,
|
||||
item: {
|
||||
...item,
|
||||
quantity: Math.min(item.variant.inventory_quantity, quantity),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const incrementItemQuantity = (id: string) => {
|
||||
const item = getItem(id)
|
||||
if (!item) return
|
||||
|
||||
dispatch({
|
||||
type: ACTION_TYPES.UPDATE_ITEM,
|
||||
payload: {
|
||||
id,
|
||||
item: {
|
||||
...item,
|
||||
quantity: Math.min(
|
||||
item.variant.inventory_quantity,
|
||||
item.quantity + 1
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const decrementItemQuantity = (id: string) => {
|
||||
const item = getItem(id)
|
||||
if (!item) return
|
||||
|
||||
dispatch({
|
||||
type: ACTION_TYPES.UPDATE_ITEM,
|
||||
payload: {
|
||||
id,
|
||||
item: { ...item, quantity: Math.max(0, item.quantity - 1) },
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const removeItem = (id: string) => {
|
||||
dispatch({
|
||||
type: ACTION_TYPES.REMOVE_ITEM,
|
||||
payload: { id },
|
||||
})
|
||||
}
|
||||
|
||||
const clearItems = () => {
|
||||
dispatch({
|
||||
type: ACTION_TYPES.CLEAR_ITEMS,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<SessionCartContext.Provider
|
||||
value={{
|
||||
...state,
|
||||
setRegion,
|
||||
addItem,
|
||||
updateItem,
|
||||
updateItemQuantity,
|
||||
incrementItemQuantity,
|
||||
decrementItemQuantity,
|
||||
removeItem,
|
||||
getItem,
|
||||
setItems,
|
||||
clearItems,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SessionCartContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useSessionCart = () => {
|
||||
const context = useContext(SessionCartContext)
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useSessionCart should be used as a child of SessionCartProvider"
|
||||
)
|
||||
}
|
||||
return context
|
||||
}
|
||||
2
packages/medusa-react/src/hooks/carts/index.ts
Normal file
2
packages/medusa-react/src/hooks/carts/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./queries"
|
||||
export * from "./mutations"
|
||||
156
packages/medusa-react/src/hooks/carts/mutations.ts
Normal file
156
packages/medusa-react/src/hooks/carts/mutations.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import {
|
||||
StoreCartsRes,
|
||||
StoreCompleteCartRes,
|
||||
StorePostCartReq,
|
||||
StorePostCartsCartPaymentSessionReq,
|
||||
StorePostCartsCartPaymentSessionUpdateReq,
|
||||
StorePostCartsCartReq,
|
||||
StorePostCartsCartShippingMethodReq,
|
||||
} from "@medusajs/medusa"
|
||||
import { useMutation, UseMutationOptions } from "react-query"
|
||||
import { useMedusa } from "../../contexts/medusa"
|
||||
|
||||
export const useCreateCart = (
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
StorePostCartReq | undefined
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data?: StorePostCartReq | undefined) => client.carts.create(data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export const useUpdateCart = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<StoreCartsRes, Error, StorePostCartsCartReq>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data: StorePostCartsCartReq) => client.carts.update(cartId, data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export const useCompleteCart = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<StoreCompleteCartRes, Error>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(() => client.carts.complete(cartId), options)
|
||||
}
|
||||
|
||||
export const useCreatePaymentSession = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<StoreCartsRes, Error>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(() => client.carts.createPaymentSessions(cartId), options)
|
||||
}
|
||||
|
||||
export const useUpdatePaymentSession = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
{ provider_id: string } & StorePostCartsCartPaymentSessionUpdateReq
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(
|
||||
data: { provider_id: string } & StorePostCartsCartPaymentSessionUpdateReq
|
||||
) => client.carts.updatePaymentSession(cartId, data.provider_id, data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
type RefreshPaymentSessionMutationData = {
|
||||
provider_id: string
|
||||
}
|
||||
|
||||
export const useRefreshPaymentSession = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
RefreshPaymentSessionMutationData
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
({ provider_id }: RefreshPaymentSessionMutationData) =>
|
||||
client.carts.refreshPaymentSession(cartId, provider_id),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
type SetPaymentSessionMutationData = { provider_id: string }
|
||||
|
||||
export const useSetPaymentSession = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
SetPaymentSessionMutationData
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data: StorePostCartsCartPaymentSessionReq) =>
|
||||
client.carts.setPaymentSession(cartId, data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export const useAddShippingMethodToCart = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
StorePostCartsCartShippingMethodReq
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data: StorePostCartsCartShippingMethodReq) =>
|
||||
client.carts.addShippingMethod(cartId, data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
type DeletePaymentSessionMutationData = {
|
||||
provider_id: string
|
||||
}
|
||||
|
||||
export const useDeletePaymentSession = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
DeletePaymentSessionMutationData
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
({ provider_id }: DeletePaymentSessionMutationData) =>
|
||||
client.carts.deletePaymentSession(cartId, provider_id),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export const useStartCheckout = (
|
||||
options?: UseMutationOptions<StoreCartsRes["cart"], Error, StorePostCartReq>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const mutation = useMutation(async (data?: StorePostCartReq) => {
|
||||
const { cart } = await client.carts.create(data)
|
||||
const res = await client.carts.createPaymentSessions(cart.id)
|
||||
return res.cart
|
||||
}, options)
|
||||
|
||||
return mutation
|
||||
}
|
||||
28
packages/medusa-react/src/hooks/carts/queries.ts
Normal file
28
packages/medusa-react/src/hooks/carts/queries.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
import { StoreCartsRes } from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts/medusa"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
|
||||
const CARTS_QUERY_KEY = `carts` as const
|
||||
|
||||
export const cartKeys = makeKeysFactory(CARTS_QUERY_KEY)
|
||||
type CartQueryKey = typeof cartKeys
|
||||
|
||||
export const useGetCart = (
|
||||
id: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreCartsRes>,
|
||||
Error,
|
||||
ReturnType<CartQueryKey["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
cartKeys.detail(id),
|
||||
() => client.carts.retrieve(id),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
1
packages/medusa-react/src/hooks/collections/index.ts
Normal file
1
packages/medusa-react/src/hooks/collections/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./queries"
|
||||
50
packages/medusa-react/src/hooks/collections/queries.ts
Normal file
50
packages/medusa-react/src/hooks/collections/queries.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
StoreCollectionsListRes,
|
||||
StoreCollectionsRes,
|
||||
StoreGetCollectionsParams,
|
||||
} from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts/medusa"
|
||||
import { UseQueryOptionsWrapper } from "./../../types"
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
|
||||
const COLLECTIONS_QUERY_KEY = `collections` as const
|
||||
|
||||
export const collectionKeys = makeKeysFactory(COLLECTIONS_QUERY_KEY)
|
||||
|
||||
type CollectionQueryKey = typeof collectionKeys
|
||||
|
||||
export const useCollection = (
|
||||
id: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreCollectionsRes>,
|
||||
Error,
|
||||
ReturnType<CollectionQueryKey["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
collectionKeys.detail(id),
|
||||
() => client.collections.retrieve(id),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useCollections = (
|
||||
query?: StoreGetCollectionsParams,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreCollectionsListRes>,
|
||||
Error,
|
||||
ReturnType<CollectionQueryKey["list"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
collectionKeys.list(query),
|
||||
() => client.collections.list(query),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
2
packages/medusa-react/src/hooks/customers/index.ts
Normal file
2
packages/medusa-react/src/hooks/customers/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./queries"
|
||||
export * from "./mutations"
|
||||
32
packages/medusa-react/src/hooks/customers/mutations.ts
Normal file
32
packages/medusa-react/src/hooks/customers/mutations.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
StoreCustomersRes,
|
||||
StorePostCustomersCustomerReq,
|
||||
StorePostCustomersReq,
|
||||
} from "@medusajs/medusa"
|
||||
import { useMutation, UseMutationOptions } from "react-query"
|
||||
import { useMedusa } from "../../contexts/medusa"
|
||||
|
||||
export const useCreateCustomer = (
|
||||
options?: UseMutationOptions<StoreCustomersRes, Error, StorePostCustomersReq>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data: StorePostCustomersReq) => client.customers.create(data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export const useUpdateMe = (
|
||||
options?: UseMutationOptions<
|
||||
StoreCustomersRes,
|
||||
Error,
|
||||
{ id: string } & StorePostCustomersCustomerReq
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
({ id, ...data }: { id: string } & StorePostCustomersCustomerReq) =>
|
||||
client.customers.update(data),
|
||||
options
|
||||
)
|
||||
}
|
||||
53
packages/medusa-react/src/hooks/customers/queries.ts
Normal file
53
packages/medusa-react/src/hooks/customers/queries.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
StoreCustomersListOrdersRes,
|
||||
StoreCustomersRes,
|
||||
StoreGetCustomersCustomerOrdersParams,
|
||||
} from "@medusajs/medusa"
|
||||
import { useQuery } from "react-query"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
|
||||
const CUSTOMERS_QUERY_KEY = `customers` as const
|
||||
|
||||
export const customerKeys = {
|
||||
...makeKeysFactory(CUSTOMERS_QUERY_KEY),
|
||||
orders: (id: string) => [...customerKeys.detail(id), "orders"] as const,
|
||||
}
|
||||
|
||||
type CustomerQueryKey = typeof customerKeys
|
||||
|
||||
export const useMeCustomer = (
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreCustomersRes>,
|
||||
Error,
|
||||
ReturnType<CustomerQueryKey["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
customerKeys.detail("me"),
|
||||
() => client.customers.retrieve(),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useCustomerOrders = (
|
||||
query: StoreGetCustomersCustomerOrdersParams = { limit: 10, offset: 0 },
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreCustomersListOrdersRes>,
|
||||
Error,
|
||||
ReturnType<CustomerQueryKey["orders"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
customerKeys.orders("me"),
|
||||
() => client.customers.listOrders(query),
|
||||
options
|
||||
)
|
||||
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
1
packages/medusa-react/src/hooks/gift-cards/index.ts
Normal file
1
packages/medusa-react/src/hooks/gift-cards/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./queries"
|
||||
29
packages/medusa-react/src/hooks/gift-cards/queries.ts
Normal file
29
packages/medusa-react/src/hooks/gift-cards/queries.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
import { StoreGiftCardsRes } from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
|
||||
const GIFT_CARDS_QUERY_KEY = `gift_cards` as const
|
||||
|
||||
export const giftCardKeys = makeKeysFactory(GIFT_CARDS_QUERY_KEY)
|
||||
|
||||
type GiftCardQueryKey = typeof giftCardKeys
|
||||
|
||||
export const useGiftCard = (
|
||||
id: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreGiftCardsRes>,
|
||||
Error,
|
||||
ReturnType<GiftCardQueryKey["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
giftCardKeys.detail(id),
|
||||
() => client.giftCards.retrieve(id),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
13
packages/medusa-react/src/hooks/index.ts
Normal file
13
packages/medusa-react/src/hooks/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export * from "./products/"
|
||||
export * from "./carts/"
|
||||
export * from "./shipping-options/"
|
||||
export * from "./regions/"
|
||||
export * from "./return-reasons/"
|
||||
export * from "./swaps/"
|
||||
export * from "./carts/"
|
||||
export * from "./orders/"
|
||||
export * from "./customers/"
|
||||
export * from "./returns/"
|
||||
export * from "./gift-cards/"
|
||||
export * from "./line-items/"
|
||||
export * from "./collections"
|
||||
1
packages/medusa-react/src/hooks/line-items/index.ts
Normal file
1
packages/medusa-react/src/hooks/line-items/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./mutations"
|
||||
54
packages/medusa-react/src/hooks/line-items/mutations.ts
Normal file
54
packages/medusa-react/src/hooks/line-items/mutations.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
StoreCartsRes,
|
||||
StorePostCartsCartLineItemsReq,
|
||||
StorePostCartsCartLineItemsItemReq,
|
||||
} from "@medusajs/medusa"
|
||||
import { useMutation, UseMutationOptions } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
|
||||
export const useCreateLineItem = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
StorePostCartsCartLineItemsReq
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data: StorePostCartsCartLineItemsReq) =>
|
||||
client.carts.lineItems.create(cartId, data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export const useUpdateLineItem = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
StorePostCartsCartLineItemsItemReq & { lineId: string }
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
({
|
||||
lineId,
|
||||
...data
|
||||
}: StorePostCartsCartLineItemsItemReq & { lineId: string }) =>
|
||||
client.carts.lineItems.update(cartId, lineId, data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export const useDeleteLineItem = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<StoreCartsRes, Error, { lineId: string }>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
({ lineId }: { lineId: string }) =>
|
||||
client.carts.lineItems.delete(cartId, lineId),
|
||||
options
|
||||
)
|
||||
}
|
||||
1
packages/medusa-react/src/hooks/orders/index.ts
Normal file
1
packages/medusa-react/src/hooks/orders/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./queries"
|
||||
71
packages/medusa-react/src/hooks/orders/queries.ts
Normal file
71
packages/medusa-react/src/hooks/orders/queries.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
import { StoreOrdersRes, StoreGetOrdersParams } from "@medusajs/medusa"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
|
||||
const ORDERS_QUERY_KEY = `orders` as const
|
||||
|
||||
export const orderKeys = {
|
||||
...makeKeysFactory<typeof ORDERS_QUERY_KEY, StoreGetOrdersParams>(
|
||||
ORDERS_QUERY_KEY
|
||||
),
|
||||
cart: (cartId: string) => [...orderKeys.details(), "cart", cartId] as const,
|
||||
}
|
||||
|
||||
type OrderQueryKey = typeof orderKeys
|
||||
|
||||
export const useOrder = (
|
||||
id: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreOrdersRes>,
|
||||
Error,
|
||||
ReturnType<OrderQueryKey["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
orderKeys.detail(id),
|
||||
() => client.orders.retrieve(id),
|
||||
options
|
||||
)
|
||||
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useCartOrder = (
|
||||
cartId: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreOrdersRes>,
|
||||
Error,
|
||||
ReturnType<OrderQueryKey["cart"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
orderKeys.cart(cartId),
|
||||
() => client.orders.retrieveByCartId(cartId),
|
||||
options
|
||||
)
|
||||
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useOrderLookup = (
|
||||
query: StoreGetOrdersParams,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreOrdersRes>,
|
||||
Error,
|
||||
ReturnType<OrderQueryKey["list"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
orderKeys.list(query),
|
||||
() => client.orders.lookupOrder(query),
|
||||
options
|
||||
)
|
||||
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
1
packages/medusa-react/src/hooks/products/index.ts
Normal file
1
packages/medusa-react/src/hooks/products/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./queries"
|
||||
53
packages/medusa-react/src/hooks/products/queries.ts
Normal file
53
packages/medusa-react/src/hooks/products/queries.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import {
|
||||
StoreGetProductsParams,
|
||||
StoreProductsListRes,
|
||||
StoreProductsRes,
|
||||
} from "@medusajs/medusa"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
|
||||
const PRODUCTS_QUERY_KEY = `products` as const
|
||||
|
||||
export const productKeys = makeKeysFactory<
|
||||
typeof PRODUCTS_QUERY_KEY,
|
||||
StoreGetProductsParams
|
||||
>(PRODUCTS_QUERY_KEY)
|
||||
type ProductQueryKey = typeof productKeys
|
||||
|
||||
export const useProducts = (
|
||||
query?: StoreGetProductsParams,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreProductsListRes>,
|
||||
Error,
|
||||
ReturnType<ProductQueryKey["list"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
productKeys.list(query),
|
||||
() => client.products.list(query),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useProduct = (
|
||||
id: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreProductsRes>,
|
||||
Error,
|
||||
ReturnType<ProductQueryKey["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
productKeys.detail(id),
|
||||
() => client.products.retrieve(id),
|
||||
options
|
||||
)
|
||||
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
1
packages/medusa-react/src/hooks/regions/index.ts
Normal file
1
packages/medusa-react/src/hooks/regions/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./queries"
|
||||
45
packages/medusa-react/src/hooks/regions/queries.ts
Normal file
45
packages/medusa-react/src/hooks/regions/queries.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import { StoreRegionsRes, StoreRegionsListRes } from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
|
||||
const REGIONS_QUERY_KEY = `regions` as const
|
||||
|
||||
const regionsKey = makeKeysFactory(REGIONS_QUERY_KEY)
|
||||
|
||||
type RegionQueryType = typeof regionsKey
|
||||
|
||||
export const useRegions = (
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreRegionsListRes>,
|
||||
Error,
|
||||
ReturnType<RegionQueryType["lists"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
regionsKey.lists(),
|
||||
() => client.regions.list(),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useRegion = (
|
||||
id: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreRegionsRes>,
|
||||
Error,
|
||||
ReturnType<RegionQueryType["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
regionsKey.detail(id),
|
||||
() => client.regions.retrieve(id),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
1
packages/medusa-react/src/hooks/return-reasons/index.ts
Normal file
1
packages/medusa-react/src/hooks/return-reasons/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./queries"
|
||||
48
packages/medusa-react/src/hooks/return-reasons/queries.ts
Normal file
48
packages/medusa-react/src/hooks/return-reasons/queries.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
import {
|
||||
StoreReturnReasonsListRes,
|
||||
StoreReturnReasonsRes,
|
||||
} from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
|
||||
const RETURNS_REASONS_QUERY_KEY = `return_reasons` as const
|
||||
|
||||
const returnReasonsKey = makeKeysFactory(RETURNS_REASONS_QUERY_KEY)
|
||||
|
||||
type ReturnReasonsQueryKey = typeof returnReasonsKey
|
||||
|
||||
export const useReturnReasons = (
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreReturnReasonsListRes>,
|
||||
Error,
|
||||
ReturnType<ReturnReasonsQueryKey["lists"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
returnReasonsKey.lists(),
|
||||
() => client.returnReasons.list(),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useReturnReason = (
|
||||
id: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreReturnReasonsRes>,
|
||||
Error,
|
||||
ReturnType<ReturnReasonsQueryKey["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
returnReasonsKey.detail(id),
|
||||
() => client.returnReasons.retrieve(id),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
1
packages/medusa-react/src/hooks/returns/index.ts
Normal file
1
packages/medusa-react/src/hooks/returns/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./mutations"
|
||||
13
packages/medusa-react/src/hooks/returns/mutations.ts
Normal file
13
packages/medusa-react/src/hooks/returns/mutations.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { StoreReturnsRes, StorePostReturnsReq } from "@medusajs/medusa"
|
||||
import { useMutation, UseMutationOptions } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
|
||||
export const useCreateReturn = (
|
||||
options?: UseMutationOptions<StoreReturnsRes, Error, StorePostReturnsReq>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data: StorePostReturnsReq) => client.returns.create(data),
|
||||
options
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./queries"
|
||||
52
packages/medusa-react/src/hooks/shipping-options/queries.ts
Normal file
52
packages/medusa-react/src/hooks/shipping-options/queries.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import {
|
||||
StoreShippingOptionsListRes,
|
||||
StoreGetShippingOptionsParams,
|
||||
} from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
|
||||
const SHIPPING_OPTION_QUERY_KEY = `shipping_options` as const
|
||||
|
||||
const shippingOptionKey = {
|
||||
...makeKeysFactory(SHIPPING_OPTION_QUERY_KEY),
|
||||
cart: (cartId: string) => [...shippingOptionKey.all, "cart", cartId] as const,
|
||||
}
|
||||
|
||||
type ShippingOptionQueryKey = typeof shippingOptionKey
|
||||
|
||||
export const useShippingOptions = (
|
||||
query?: StoreGetShippingOptionsParams,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreShippingOptionsListRes>,
|
||||
Error,
|
||||
ReturnType<ShippingOptionQueryKey["list"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
shippingOptionKey.list(query),
|
||||
async () => client.shippingOptions.list(query),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useCartShippingOptions = (
|
||||
cartId: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreShippingOptionsListRes>,
|
||||
Error,
|
||||
ReturnType<ShippingOptionQueryKey["cart"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
shippingOptionKey.cart(cartId),
|
||||
async () => client.shippingOptions.listCartOptions(cartId),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
2
packages/medusa-react/src/hooks/swaps/index.ts
Normal file
2
packages/medusa-react/src/hooks/swaps/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./queries"
|
||||
export * from "./mutations"
|
||||
13
packages/medusa-react/src/hooks/swaps/mutations.ts
Normal file
13
packages/medusa-react/src/hooks/swaps/mutations.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { StoreSwapsRes, StorePostSwapsReq } from "@medusajs/medusa"
|
||||
import { useMutation, UseMutationOptions } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
|
||||
export const useCreateSwap = (
|
||||
options?: UseMutationOptions<StoreSwapsRes, Error, StorePostSwapsReq>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data: StorePostSwapsReq) => client.swaps.create(data),
|
||||
options
|
||||
)
|
||||
}
|
||||
33
packages/medusa-react/src/hooks/swaps/queries.ts
Normal file
33
packages/medusa-react/src/hooks/swaps/queries.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
import { StoreSwapsRes } from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
|
||||
const SWAPS_QUERY_KEY = `swaps` as const
|
||||
|
||||
const swapKey = {
|
||||
...makeKeysFactory(SWAPS_QUERY_KEY),
|
||||
cart: (cartId: string) => [...swapKey.all, "cart", cartId] as const,
|
||||
}
|
||||
|
||||
type SwapQueryKey = typeof swapKey
|
||||
|
||||
export const useCartSwap = (
|
||||
cartId: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreSwapsRes>,
|
||||
Error,
|
||||
ReturnType<SwapQueryKey["cart"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
swapKey.cart(cartId),
|
||||
() => client.swaps.retrieveByCartId(cartId),
|
||||
options
|
||||
)
|
||||
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
62
packages/medusa-react/src/hooks/utils/index.ts
Normal file
62
packages/medusa-react/src/hooks/utils/index.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import * as React from "react"
|
||||
|
||||
type TQueryKey<TKey, TListQuery = any, TDetailQuery = string> = {
|
||||
all: [TKey]
|
||||
lists: () => [...TQueryKey<TKey>["all"], "list"]
|
||||
list: (
|
||||
query?: TListQuery
|
||||
) => [
|
||||
...ReturnType<TQueryKey<TKey>["lists"]>,
|
||||
{ query: TListQuery | undefined }
|
||||
]
|
||||
details: () => [...TQueryKey<TKey>["all"], "detail"]
|
||||
detail: (
|
||||
id: TDetailQuery
|
||||
) => [...ReturnType<TQueryKey<TKey>["details"]>, TDetailQuery]
|
||||
}
|
||||
|
||||
export const makeKeysFactory = <
|
||||
T,
|
||||
TListQueryType = any,
|
||||
TDetailQueryType = string
|
||||
>(
|
||||
globalKey: T
|
||||
) => {
|
||||
const queryKeyFactory: TQueryKey<T, TListQueryType, TDetailQueryType> = {
|
||||
all: [globalKey],
|
||||
lists: () => [...queryKeyFactory.all, "list"],
|
||||
list: (query?: TListQueryType) => [...queryKeyFactory.lists(), { query }],
|
||||
details: () => [...queryKeyFactory.all, "detail"],
|
||||
detail: (id: TDetailQueryType) => [...queryKeyFactory.details(), id],
|
||||
}
|
||||
return queryKeyFactory
|
||||
}
|
||||
|
||||
export const useLocalStorage = (key: string, initialState: string) => {
|
||||
const [item, setItem] = React.useState(() => {
|
||||
try {
|
||||
const item =
|
||||
typeof window !== "undefined" && window.localStorage.getItem(key)
|
||||
|
||||
return item || initialState
|
||||
} catch (err) {
|
||||
return initialState
|
||||
}
|
||||
})
|
||||
|
||||
const save = (data: string) => {
|
||||
setItem(data)
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
window.localStorage.setItem(key, data)
|
||||
}
|
||||
}
|
||||
|
||||
const remove = () => {
|
||||
if (typeof window !== "undefined") {
|
||||
window.localStorage.removeItem(key)
|
||||
}
|
||||
}
|
||||
|
||||
return [item, save, remove] as const
|
||||
}
|
||||
3
packages/medusa-react/src/index.ts
Normal file
3
packages/medusa-react/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./contexts"
|
||||
export * from "./hooks/"
|
||||
export * from "./utils/"
|
||||
31
packages/medusa-react/src/types.ts
Normal file
31
packages/medusa-react/src/types.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import {
|
||||
Region,
|
||||
ProductVariant as ProductVariantEntity,
|
||||
StoreCartsRes,
|
||||
} from "@medusajs/medusa"
|
||||
import { QueryKey, UseQueryOptions } from "react-query"
|
||||
|
||||
export type UseQueryOptionsWrapper<
|
||||
// Return type of queryFn
|
||||
TQueryFn = unknown,
|
||||
// Type thrown in case the queryFn rejects
|
||||
E = Error,
|
||||
// Query key type
|
||||
TQueryKey extends QueryKey = QueryKey
|
||||
> = Omit<
|
||||
UseQueryOptions<TQueryFn, E, TQueryFn, TQueryKey>,
|
||||
"queryKey" | "queryFn" | "select" | "refetchInterval"
|
||||
>
|
||||
|
||||
// Choose only a subset of the type Region to allow for some flexibility
|
||||
export type RegionInfo = Pick<Region, "currency_code" | "tax_code" | "tax_rate">
|
||||
export type ProductVariant = ConvertDateToString<
|
||||
Omit<ProductVariantEntity, "beforeInsert">
|
||||
>
|
||||
export type ProductVariantInfo = Pick<ProductVariant, "prices">
|
||||
|
||||
type ConvertDateToString<T extends {}> = {
|
||||
[P in keyof T]: T[P] extends Date ? Date | string : T[P]
|
||||
}
|
||||
|
||||
export type Cart = StoreCartsRes["cart"]
|
||||
168
packages/medusa-react/src/utils/index.ts
Normal file
168
packages/medusa-react/src/utils/index.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
import { isEmpty } from "lodash"
|
||||
import { RegionInfo, ProductVariantInfo } from "../types"
|
||||
|
||||
type FormatVariantPriceParams = {
|
||||
variant: ProductVariantInfo
|
||||
region: RegionInfo
|
||||
includeTaxes?: boolean
|
||||
minimumFractionDigits?: number
|
||||
maximumFractionDigits?: number
|
||||
locale?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a product variant and a region, and converts the variant's price to a localized decimal format
|
||||
*/
|
||||
export const formatVariantPrice = ({
|
||||
variant,
|
||||
region,
|
||||
includeTaxes = true,
|
||||
...rest
|
||||
}: FormatVariantPriceParams) => {
|
||||
const amount = computeVariantPrice({ variant, region, includeTaxes })
|
||||
|
||||
return convertToLocale({
|
||||
amount,
|
||||
currency_code: region?.currency_code,
|
||||
...rest,
|
||||
})
|
||||
}
|
||||
|
||||
type ComputeVariantPriceParams = {
|
||||
variant: ProductVariantInfo
|
||||
region: RegionInfo
|
||||
includeTaxes?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a product variant and region, and returns the variant price as a decimal number
|
||||
* @param params.variant - product variant
|
||||
* @param params.region - region
|
||||
* @param params.includeTaxes - whether to include taxes or not
|
||||
*/
|
||||
export const computeVariantPrice = ({
|
||||
variant,
|
||||
region,
|
||||
includeTaxes = true,
|
||||
}: ComputeVariantPriceParams) => {
|
||||
const amount = getVariantPrice(variant, region)
|
||||
|
||||
return computeAmount({
|
||||
amount,
|
||||
region,
|
||||
includeTaxes,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the price amount correspoding to the region selected
|
||||
* @param variant - the product variant
|
||||
* @param region - the region
|
||||
* @returns - the price's amount
|
||||
*/
|
||||
export const getVariantPrice = (
|
||||
variant: ProductVariantInfo,
|
||||
region: RegionInfo
|
||||
) => {
|
||||
let price = variant?.prices?.find(
|
||||
(p) =>
|
||||
p.currency_code.toLowerCase() === region?.currency_code?.toLowerCase()
|
||||
)
|
||||
|
||||
return price?.amount || 0
|
||||
}
|
||||
|
||||
type ComputeAmountParams = {
|
||||
amount: number
|
||||
region: RegionInfo
|
||||
includeTaxes?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an amount, a region, and returns the amount as a decimal including or excluding taxes
|
||||
*/
|
||||
export const computeAmount = ({
|
||||
amount,
|
||||
region,
|
||||
includeTaxes = true,
|
||||
}: ComputeAmountParams) => {
|
||||
const toDecimal = convertToDecimal(amount, region)
|
||||
|
||||
const taxRate = includeTaxes ? getTaxRate(region) : 0
|
||||
|
||||
const amountWithTaxes = toDecimal * (1 + taxRate)
|
||||
|
||||
return amountWithTaxes
|
||||
}
|
||||
|
||||
type FormatAmountParams = {
|
||||
amount: number
|
||||
region: RegionInfo
|
||||
includeTaxes?: boolean
|
||||
minimumFractionDigits?: number
|
||||
maximumFractionDigits?: number
|
||||
locale?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an amount and a region, and converts the amount to a localized decimal format
|
||||
*/
|
||||
export const formatAmount = ({
|
||||
amount,
|
||||
region,
|
||||
includeTaxes = true,
|
||||
...rest
|
||||
}: FormatAmountParams) => {
|
||||
const taxAwareAmount = computeAmount({
|
||||
amount,
|
||||
region,
|
||||
includeTaxes,
|
||||
})
|
||||
return convertToLocale({
|
||||
amount: taxAwareAmount,
|
||||
currency_code: region.currency_code,
|
||||
...rest,
|
||||
})
|
||||
}
|
||||
|
||||
// we should probably add a more extensive list
|
||||
const noDivisionCurrencies = ["krw", "jpy", "vnd"]
|
||||
|
||||
const convertToDecimal = (amount: number, region: RegionInfo) => {
|
||||
const divisor = noDivisionCurrencies.includes(
|
||||
region?.currency_code?.toLowerCase()
|
||||
)
|
||||
? 1
|
||||
: 100
|
||||
|
||||
return Math.floor(amount) / divisor
|
||||
}
|
||||
|
||||
const getTaxRate = (region?: RegionInfo) => {
|
||||
return region && !isEmpty(region) ? region?.tax_rate / 100 : 0
|
||||
}
|
||||
|
||||
const convertToLocale = ({
|
||||
amount,
|
||||
currency_code,
|
||||
minimumFractionDigits,
|
||||
maximumFractionDigits,
|
||||
locale = "en-US",
|
||||
}: ConvertToLocaleParams) => {
|
||||
return currency_code && !isEmpty(currency_code)
|
||||
? new Intl.NumberFormat(locale, {
|
||||
style: "currency",
|
||||
currency: currency_code,
|
||||
minimumFractionDigits,
|
||||
maximumFractionDigits,
|
||||
}).format(amount)
|
||||
: amount.toString()
|
||||
}
|
||||
|
||||
type ConvertToLocaleParams = {
|
||||
amount: number
|
||||
currency_code: string
|
||||
minimumFractionDigits?: number
|
||||
maximumFractionDigits?: number
|
||||
locale?: string
|
||||
}
|
||||
Reference in New Issue
Block a user