4
0
forked from crowetic/commerce

Run prettier fix on all files (#581)

This commit is contained in:
Gonzalo Pozzo 2021-11-25 09:17:13 -03:00 committed by GitHub
parent 96e990268d
commit 73470c9232
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 904 additions and 845 deletions

View File

@ -43,8 +43,8 @@ const FeatureBar = dynamic(
) )
const Modal = dynamic( const Modal = dynamic(
() => import('@components/ui/Modal'), () => import('@components/ui/Modal'),
Object.assign(dynamicProps, {ssr: false}) Object.assign(dynamicProps, { ssr: false })
) )
interface Props { interface Props {

View File

@ -28,9 +28,16 @@ const UserNav: FC<Props> = ({ className }) => {
<ul className={s.list}> <ul className={s.list}>
{process.env.COMMERCE_CART_ENABLED && ( {process.env.COMMERCE_CART_ENABLED && (
<li className={s.item}> <li className={s.item}>
<Button className={s.item} variant="naked" onClick={toggleSidebar} aria-label={`Cart items: ${itemsCount}`}> <Button
className={s.item}
variant="naked"
onClick={toggleSidebar}
aria-label={`Cart items: ${itemsCount}`}
>
<Bag /> <Bag />
{itemsCount > 0 && <span className={s.bagCount}>{itemsCount}</span>} {itemsCount > 0 && (
<span className={s.bagCount}>{itemsCount}</span>
)}
</Button> </Button>
</li> </li>
)} )}

View File

@ -44,7 +44,7 @@ const Swatch: React.FC<Omit<ButtonProps, 'variant'> & SwatchProps> = React.memo(
<Button <Button
role="option" role="option"
aria-selected={active} aria-selected={active}
aria-label={(variant && label) ? `${variant} ${label}` : "Variant Swatch"} aria-label={variant && label ? `${variant} ${label}` : 'Variant Swatch'}
className={swatchClassName} className={swatchClassName}
{...(label && color && { title: label })} {...(label && color && { title: label })}
style={color ? { backgroundColor: color } : {}} style={color ? { backgroundColor: color } : {}}

View File

@ -28,7 +28,7 @@ const Sidebar: FC<SidebarProps> = ({ children, onClose }) => {
} }
const contentElement = contentRef.current const contentElement = contentRef.current
if (contentElement) { if (contentElement) {
disableBodyScroll(contentElement, { reserveScrollBarGap: true }) disableBodyScroll(contentElement, { reserveScrollBarGap: true })
} }

View File

@ -14,7 +14,6 @@
@apply pt-1 pb-2 text-2xl font-bold tracking-wide cursor-pointer mb-2; @apply pt-1 pb-2 text-2xl font-bold tracking-wide cursor-pointer mb-2;
} }
/* Apply base font sizes and styles for typography markup (h2, h2, ul, p, etc.). /* Apply base font sizes and styles for typography markup (h2, h2, ul, p, etc.).
A helpful addition for whenn page content is consumed from a source managed through a wysiwyg editor. */ A helpful addition for whenn page content is consumed from a source managed through a wysiwyg editor. */

View File

@ -24,36 +24,33 @@ export const getLoggedInCustomerQuery = /* GraphQL */ `
export type Customer = NonNullable<GetLoggedInCustomerQuery['customer']> export type Customer = NonNullable<GetLoggedInCustomerQuery['customer']>
const getLoggedInCustomer: CustomerEndpoint['handlers']['getLoggedInCustomer'] = async ({ const getLoggedInCustomer: CustomerEndpoint['handlers']['getLoggedInCustomer'] =
req, async ({ req, res, config }) => {
res, const token = req.cookies[config.customerCookie]
config,
}) => {
const token = req.cookies[config.customerCookie]
if (token) { if (token) {
const { data } = await config.fetch<GetLoggedInCustomerQuery>( const { data } = await config.fetch<GetLoggedInCustomerQuery>(
getLoggedInCustomerQuery, getLoggedInCustomerQuery,
undefined, undefined,
{ {
headers: { headers: {
cookie: `${config.customerCookie}=${token}`, cookie: `${config.customerCookie}=${token}`,
}, },
}
)
const { customer } = data
if (!customer) {
return res.status(400).json({
data: null,
errors: [{ message: 'Customer not found', code: 'not_found' }],
})
} }
)
const { customer } = data
if (!customer) { return res.status(200).json({ data: { customer } })
return res.status(400).json({
data: null,
errors: [{ message: 'Customer not found', code: 'not_found' }],
})
} }
return res.status(200).json({ data: { customer } }) res.status(200).json({ data: null })
} }
res.status(200).json({ data: null })
}
export default getLoggedInCustomer export default getLoggedInCustomer

View File

@ -34,7 +34,7 @@ export interface BigcommerceConfig extends CommerceAPIConfig {
storeChannelId?: string storeChannelId?: string
storeUrl?: string storeUrl?: string
storeApiClientSecret?: string storeApiClientSecret?: string
storeHash?:string storeHash?: string
storeApiFetch<T>(endpoint: string, options?: RequestInit): Promise<T> storeApiFetch<T>(endpoint: string, options?: RequestInit): Promise<T>
} }
@ -81,8 +81,8 @@ const config: BigcommerceConfig = {
storeApiToken: STORE_API_TOKEN, storeApiToken: STORE_API_TOKEN,
storeApiClientId: STORE_API_CLIENT_ID, storeApiClientId: STORE_API_CLIENT_ID,
storeChannelId: STORE_CHANNEL_ID, storeChannelId: STORE_CHANNEL_ID,
storeUrl:STORE_URL, storeUrl: STORE_URL,
storeApiClientSecret:CLIENT_SECRET, storeApiClientSecret: CLIENT_SECRET,
storeHash: STOREFRONT_HASH, storeHash: STOREFRONT_HASH,
storeApiFetch: createFetchStoreApi(() => getCommerceApi().getConfig()), storeApiFetch: createFetchStoreApi(() => getCommerceApi().getConfig()),
} }

View File

@ -15,8 +15,7 @@ export const handler: MutationHook<LoginHook> = {
async fetcher({ input: { email, password }, options, fetch }) { async fetcher({ input: { email, password }, options, fetch }) {
if (!(email && password)) { if (!(email && password)) {
throw new CommerceError({ throw new CommerceError({
message: message: 'An email and password are required to login',
'An email and password are required to login',
}) })
} }
@ -25,16 +24,18 @@ export const handler: MutationHook<LoginHook> = {
body: { email, password }, body: { email, password },
}) })
}, },
useHook: ({ fetch }) => () => { useHook:
const { revalidate } = useCustomer() ({ fetch }) =>
() => {
const { revalidate } = useCustomer()
return useCallback( return useCallback(
async function login(input) { async function login(input) {
const data = await fetch({ input }) const data = await fetch({ input })
await revalidate() await revalidate()
return data return data
}, },
[fetch, revalidate] [fetch, revalidate]
) )
}, },
} }

View File

@ -11,16 +11,18 @@ export const handler: MutationHook<LogoutHook> = {
url: '/api/logout', url: '/api/logout',
method: 'GET', method: 'GET',
}, },
useHook: ({ fetch }) => () => { useHook:
const { mutate } = useCustomer() ({ fetch }) =>
() => {
const { mutate } = useCustomer()
return useCallback( return useCallback(
async function logout() { async function logout() {
const data = await fetch() const data = await fetch()
await mutate(null, false) await mutate(null, false)
return data return data
}, },
[fetch, mutate] [fetch, mutate]
) )
}, },
} }

View File

@ -29,16 +29,18 @@ export const handler: MutationHook<SignupHook> = {
body: { firstName, lastName, email, password }, body: { firstName, lastName, email, password },
}) })
}, },
useHook: ({ fetch }) => () => { useHook:
const { revalidate } = useCustomer() ({ fetch }) =>
() => {
const { revalidate } = useCustomer()
return useCallback( return useCallback(
async function signup(input) { async function signup(input) {
const data = await fetch({ input }) const data = await fetch({ input })
await revalidate() await revalidate()
return data return data
}, },
[fetch, revalidate] [fetch, revalidate]
) )
}, },
} }

View File

@ -29,16 +29,18 @@ export const handler: MutationHook<AddItemHook> = {
return data return data
}, },
useHook: ({ fetch }) => () => { useHook:
const { mutate } = useCart() ({ fetch }) =>
() => {
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 })
await mutate(data, false) await mutate(data, false)
return data return data
}, },
[fetch, mutate] [fetch, mutate]
) )
}, },
} }

View File

@ -10,22 +10,24 @@ export const handler: SWRHook<GetCartHook> = {
url: '/api/cart', url: '/api/cart',
method: 'GET', method: 'GET',
}, },
useHook: ({ useData }) => (input) => { useHook:
const response = useData({ ({ useData }) =>
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, (input) => {
}) const response = useData({
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
})
return useMemo( return useMemo(
() => () =>
Object.create(response, { Object.create(response, {
isEmpty: { isEmpty: {
get() { get() {
return (response.data?.lineItems.length ?? 0) <= 0 return (response.data?.lineItems.length ?? 0) <= 0
},
enumerable: true,
}, },
enumerable: true, }),
}, [response]
}), )
[response] },
)
},
} }

View File

@ -30,27 +30,25 @@ export const handler = {
}: HookFetcherContext<RemoveItemHook>) { }: HookFetcherContext<RemoveItemHook>) {
return await fetch({ ...options, body: { itemId } }) return await fetch({ ...options, body: { itemId } })
}, },
useHook: ({ fetch }: MutationHookContext<RemoveItemHook>) => < useHook:
T extends LineItem | undefined = undefined ({ fetch }: MutationHookContext<RemoveItemHook>) =>
>( <T extends LineItem | undefined = undefined>(ctx: { item?: T } = {}) => {
ctx: { item?: T } = {} const { item } = ctx
) => { const { mutate } = useCart()
const { item } = ctx const removeItem: RemoveItemFn<LineItem> = async (input) => {
const { mutate } = useCart() const itemId = input?.id ?? item?.id
const removeItem: RemoveItemFn<LineItem> = async (input) => {
const itemId = input?.id ?? item?.id
if (!itemId) { if (!itemId) {
throw new ValidationError({ throw new ValidationError({
message: 'Invalid input used for this operation', message: 'Invalid input used for this operation',
}) })
}
const data = await fetch({ input: { itemId } })
await mutate(data, false)
return data
} }
const data = await fetch({ input: { itemId } }) return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate])
await mutate(data, false) },
return data
}
return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate])
},
} }

View File

@ -46,39 +46,39 @@ export const handler = {
body: { itemId, item }, body: { itemId, item },
}) })
}, },
useHook: ({ fetch }: MutationHookContext<UpdateItemHook>) => < useHook:
T extends LineItem | undefined = undefined ({ fetch }: MutationHookContext<UpdateItemHook>) =>
>( <T extends LineItem | undefined = undefined>(
ctx: { ctx: {
item?: T item?: T
wait?: number wait?: number
} = {} } = {}
) => { ) => {
const { item } = ctx const { item } = ctx
const { mutate } = useCart() as any const { mutate } = useCart() as any
return useCallback( return useCallback(
debounce(async (input: UpdateItemActionInput<T>) => { debounce(async (input: UpdateItemActionInput<T>) => {
const itemId = input.id ?? item?.id const itemId = input.id ?? item?.id
const productId = input.productId ?? item?.productId const productId = input.productId ?? item?.productId
const variantId = input.productId ?? item?.variantId const variantId = input.productId ?? item?.variantId
if (!itemId || !productId || !variantId) { if (!itemId || !productId || !variantId) {
throw new ValidationError({ throw new ValidationError({
message: 'Invalid input used for this operation', message: 'Invalid input used for this operation',
})
}
const data = await fetch({
input: {
itemId,
item: { productId, variantId, quantity: input.quantity },
},
}) })
} await mutate(data, false)
return data
const data = await fetch({ }, ctx.wait ?? 500),
input: { [fetch, mutate]
itemId, )
item: { productId, variantId, quantity: input.quantity }, },
},
})
await mutate(data, false)
return data
}, ctx.wait ?? 500),
[fetch, mutate]
)
},
} }

View File

@ -13,12 +13,14 @@ export const handler: SWRHook<CustomerHook> = {
const data = await fetch(options) const data = await fetch(options)
return data?.customer ?? null return data?.customer ?? null
}, },
useHook: ({ useData }) => (input) => { useHook:
return useData({ ({ useData }) =>
swrOptions: { (input) => {
revalidateOnFocus: false, return useData({
...input?.swrOptions, swrOptions: {
}, revalidateOnFocus: false,
}) ...input?.swrOptions,
}, },
})
},
} }

View File

@ -8,11 +8,7 @@ import getSlug from './get-slug'
function normalizeProductOption(productOption: any) { function normalizeProductOption(productOption: any) {
const { const {
node: { node: { entityId, values: { edges = [] } = {}, ...rest },
entityId,
values: { edges = [] } = {},
...rest
},
} = productOption } = productOption
return { return {

View File

@ -33,18 +33,20 @@ export const handler: SWRHook<SearchProductsHook> = {
method: options.method, method: options.method,
}) })
}, },
useHook: ({ useData }) => (input = {}) => { useHook:
return useData({ ({ useData }) =>
input: [ (input = {}) => {
['search', input.search], return useData({
['categoryId', input.categoryId], input: [
['brandId', input.brandId], ['search', input.search],
['sort', input.sort], ['categoryId', input.categoryId],
], ['brandId', input.brandId],
swrOptions: { ['sort', input.sort],
revalidateOnFocus: false, ],
...input.swrOptions, swrOptions: {
}, revalidateOnFocus: false,
}) ...input.swrOptions,
}, },
})
},
} }

View File

@ -20,4 +20,5 @@ export type WishlistTypes = {
} }
export type WishlistSchema = Core.WishlistSchema<WishlistTypes> export type WishlistSchema = Core.WishlistSchema<WishlistTypes>
export type GetCustomerWishlistOperation = Core.GetCustomerWishlistOperation<WishlistTypes> export type GetCustomerWishlistOperation =
Core.GetCustomerWishlistOperation<WishlistTypes>

View File

@ -13,25 +13,27 @@ export const handler: MutationHook<AddItemHook> = {
url: '/api/wishlist', url: '/api/wishlist',
method: 'POST', method: 'POST',
}, },
useHook: ({ fetch }) => () => { useHook:
const { data: customer } = useCustomer() ({ fetch }) =>
const { revalidate } = useWishlist() () => {
const { data: customer } = useCustomer()
const { revalidate } = useWishlist()
return useCallback( return useCallback(
async function addItem(item) { async function addItem(item) {
if (!customer) { if (!customer) {
// A signed customer is required in order to have a wishlist // A signed customer is required in order to have a wishlist
throw new CommerceError({ throw new CommerceError({
message: 'Signed customer not found', message: 'Signed customer not found',
}) })
} }
// TODO: add validations before doing the fetch // TODO: add validations before doing the fetch
const data = await fetch({ input: { item } }) const data = await fetch({ input: { item } })
await revalidate() await revalidate()
return data return data
}, },
[fetch, revalidate, customer] [fetch, revalidate, customer]
) )
}, },
} }

View File

@ -15,24 +15,26 @@ export const handler: MutationHook<RemoveItemHook> = {
url: '/api/wishlist', url: '/api/wishlist',
method: 'DELETE', method: 'DELETE',
}, },
useHook: ({ fetch }) => ({ wishlist } = {}) => { useHook:
const { data: customer } = useCustomer() ({ fetch }) =>
const { revalidate } = useWishlist(wishlist) ({ wishlist } = {}) => {
const { data: customer } = useCustomer()
const { revalidate } = useWishlist(wishlist)
return useCallback( return useCallback(
async function removeItem(input) { async function removeItem(input) {
if (!customer) { if (!customer) {
// A signed customer is required in order to have a wishlist // A signed customer is required in order to have a wishlist
throw new CommerceError({ throw new CommerceError({
message: 'Signed customer not found', message: 'Signed customer not found',
}) })
} }
const data = await fetch({ input: { itemId: String(input.id) } }) const data = await fetch({ input: { itemId: String(input.id) } })
await revalidate() await revalidate()
return data return data
}, },
[fetch, revalidate, customer] [fetch, revalidate, customer]
) )
}, },
} }

View File

@ -24,30 +24,32 @@ export const handler: SWRHook<GetWishlistHook> = {
method: options.method, method: options.method,
}) })
}, },
useHook: ({ useData }) => (input) => { useHook:
const { data: customer } = useCustomer() ({ useData }) =>
const response = useData({ (input) => {
input: [ const { data: customer } = useCustomer()
['customerId', customer?.entityId], const response = useData({
['includeProducts', input?.includeProducts], input: [
], ['customerId', customer?.entityId],
swrOptions: { ['includeProducts', input?.includeProducts],
revalidateOnFocus: false, ],
...input?.swrOptions, swrOptions: {
}, revalidateOnFocus: false,
}) ...input?.swrOptions,
},
})
return useMemo( return useMemo(
() => () =>
Object.create(response, { Object.create(response, {
isEmpty: { isEmpty: {
get() { get() {
return (response.data?.items?.length || 0) <= 0 return (response.data?.items?.length || 0) <= 0
},
enumerable: true,
}, },
enumerable: true, }),
}, [response]
}), )
[response] },
)
},
} }

View File

@ -3,60 +3,58 @@ import { CommerceAPIError } from '../utils/errors'
import isAllowedOperation from '../utils/is-allowed-operation' import isAllowedOperation from '../utils/is-allowed-operation'
import type { GetAPISchema } from '..' import type { GetAPISchema } from '..'
const cartEndpoint: GetAPISchema< const cartEndpoint: GetAPISchema<any, CartSchema<any>>['endpoint']['handler'] =
any, async (ctx) => {
CartSchema<any> const { req, res, handlers, config } = ctx
>['endpoint']['handler'] = async (ctx) => {
const { req, res, handlers, config } = ctx
if ( if (
!isAllowedOperation(req, res, { !isAllowedOperation(req, res, {
GET: handlers['getCart'], GET: handlers['getCart'],
POST: handlers['addItem'], POST: handlers['addItem'],
PUT: handlers['updateItem'], PUT: handlers['updateItem'],
DELETE: handlers['removeItem'], DELETE: handlers['removeItem'],
}) })
) { ) {
return return
}
const { cookies } = req
const cartId = cookies[config.cartCookie]
try {
// Return current cart info
if (req.method === 'GET') {
const body = { cartId }
return await handlers['getCart']({ ...ctx, body })
}
// Create or add an item to the cart
if (req.method === 'POST') {
const body = { ...req.body, cartId }
return await handlers['addItem']({ ...ctx, body })
}
// Update item in cart
if (req.method === 'PUT') {
const body = { ...req.body, cartId }
return await handlers['updateItem']({ ...ctx, body })
}
// Remove an item from the cart
if (req.method === 'DELETE') {
const body = { ...req.body, cartId }
return await handlers['removeItem']({ ...ctx, body })
}
} catch (error) {
console.error(error)
const message =
error instanceof CommerceAPIError
? 'An unexpected error ocurred with the Commerce API'
: 'An unexpected error ocurred'
res.status(500).json({ data: null, errors: [{ message }] })
}
} }
const { cookies } = req
const cartId = cookies[config.cartCookie]
try {
// Return current cart info
if (req.method === 'GET') {
const body = { cartId }
return await handlers['getCart']({ ...ctx, body })
}
// Create or add an item to the cart
if (req.method === 'POST') {
const body = { ...req.body, cartId }
return await handlers['addItem']({ ...ctx, body })
}
// Update item in cart
if (req.method === 'PUT') {
const body = { ...req.body, cartId }
return await handlers['updateItem']({ ...ctx, body })
}
// Remove an item from the cart
if (req.method === 'DELETE') {
const body = { ...req.body, cartId }
return await handlers['removeItem']({ ...ctx, body })
}
} catch (error) {
console.error(error)
const message =
error instanceof CommerceAPIError
? 'An unexpected error ocurred with the Commerce API'
: 'An unexpected error ocurred'
res.status(500).json({ data: null, errors: [{ message }] })
}
}
export default cartEndpoint export default cartEndpoint

View File

@ -3,35 +3,33 @@ import { CommerceAPIError } from '../utils/errors'
import isAllowedOperation from '../utils/is-allowed-operation' import isAllowedOperation from '../utils/is-allowed-operation'
import type { GetAPISchema } from '..' import type { GetAPISchema } from '..'
const logoutEndpoint: GetAPISchema< const logoutEndpoint: GetAPISchema<any, LogoutSchema>['endpoint']['handler'] =
any, async (ctx) => {
LogoutSchema const { req, res, handlers } = ctx
>['endpoint']['handler'] = async (ctx) => {
const { req, res, handlers } = ctx
if ( if (
!isAllowedOperation(req, res, { !isAllowedOperation(req, res, {
GET: handlers['logout'], GET: handlers['logout'],
}) })
) { ) {
return return
}
try {
const redirectTo = req.query.redirect_to
const body = typeof redirectTo === 'string' ? { redirectTo } : {}
return await handlers['logout']({ ...ctx, body })
} catch (error) {
console.error(error)
const message =
error instanceof CommerceAPIError
? 'An unexpected error ocurred with the Commerce API'
: 'An unexpected error ocurred'
res.status(500).json({ data: null, errors: [{ message }] })
}
} }
try {
const redirectTo = req.query.redirect_to
const body = typeof redirectTo === 'string' ? { redirectTo } : {}
return await handlers['logout']({ ...ctx, body })
} catch (error) {
console.error(error)
const message =
error instanceof CommerceAPIError
? 'An unexpected error ocurred with the Commerce API'
: 'An unexpected error ocurred'
res.status(500).json({ data: null, errors: [{ message }] })
}
}
export default logoutEndpoint export default logoutEndpoint

View File

@ -3,36 +3,34 @@ import { CommerceAPIError } from '../utils/errors'
import isAllowedOperation from '../utils/is-allowed-operation' import isAllowedOperation from '../utils/is-allowed-operation'
import type { GetAPISchema } from '..' import type { GetAPISchema } from '..'
const signupEndpoint: GetAPISchema< const signupEndpoint: GetAPISchema<any, SignupSchema>['endpoint']['handler'] =
any, async (ctx) => {
SignupSchema const { req, res, handlers, config } = ctx
>['endpoint']['handler'] = async (ctx) => {
const { req, res, handlers, config } = ctx
if ( if (
!isAllowedOperation(req, res, { !isAllowedOperation(req, res, {
POST: handlers['signup'], POST: handlers['signup'],
}) })
) { ) {
return return
}
const { cookies } = req
const cartId = cookies[config.cartCookie]
try {
const body = { ...req.body, cartId }
return await handlers['signup']({ ...ctx, body })
} catch (error) {
console.error(error)
const message =
error instanceof CommerceAPIError
? 'An unexpected error ocurred with the Commerce API'
: 'An unexpected error ocurred'
res.status(500).json({ data: null, errors: [{ message }] })
}
} }
const { cookies } = req
const cartId = cookies[config.cartCookie]
try {
const body = { ...req.body, cartId }
return await handlers['signup']({ ...ctx, body })
} catch (error) {
console.error(error)
const message =
error instanceof CommerceAPIError
? 'An unexpected error ocurred with the Commerce API'
: 'An unexpected error ocurred'
res.status(500).json({ data: null, errors: [{ message }] })
}
}
export default signupEndpoint export default signupEndpoint

View File

@ -1,5 +1,5 @@
export * as Card from "./card" export * as Card from './card'
export * as Address from "./address" export * as Address from './address'
// TODO: define this type // TODO: define this type
export type Customer = any export type Customer = any

View File

@ -77,12 +77,11 @@ export type ProductsSchema<T extends ProductTypes = ProductTypes> = {
} }
} }
export type GetAllProductPathsOperation< export type GetAllProductPathsOperation<T extends ProductTypes = ProductTypes> =
T extends ProductTypes = ProductTypes {
> = { data: { products: Pick<T['product'], 'path'>[] }
data: { products: Pick<T['product'], 'path'>[] } variables: { first?: number }
variables: { first?: number } }
}
export type GetAllProductsOperation<T extends ProductTypes = ProductTypes> = { export type GetAllProductsOperation<T extends ProductTypes = ProductTypes> = {
data: { products: T['product'][] } data: { products: T['product'][] }

View File

@ -11,16 +11,18 @@ type InferValue<Prop extends PropertyKey, Desc> = Desc extends {
? Record<Prop, T> ? Record<Prop, T>
: never : never
type DefineProperty<Prop extends PropertyKey, Desc extends PropertyDescriptor> = type DefineProperty<
Desc extends { writable: any; set(val: any): any } Prop extends PropertyKey,
? never Desc extends PropertyDescriptor
: Desc extends { writable: any; get(): any } > = Desc extends { writable: any; set(val: any): any }
? never ? never
: Desc extends { writable: false } : Desc extends { writable: any; get(): any }
? Readonly<InferValue<Prop, Desc>> ? never
: Desc extends { writable: true } : Desc extends { writable: false }
? InferValue<Prop, Desc> ? Readonly<InferValue<Prop, Desc>>
: Readonly<InferValue<Prop, Desc>> : Desc extends { writable: true }
? InferValue<Prop, Desc>
: Readonly<InferValue<Prop, Desc>>
export default function defineProperty< export default function defineProperty<
Obj extends object, Obj extends object,

View File

@ -9,7 +9,10 @@ import addItem from './add-item'
import updateItem from './update-item' import updateItem from './update-item'
import removeItem from './remove-item' import removeItem from './remove-item'
export type CustomerAddressAPI = GetAPISchema<OrdercloudAPI, CustomerAddressSchema> export type CustomerAddressAPI = GetAPISchema<
OrdercloudAPI,
CustomerAddressSchema
>
export type CustomerAddressEndpoint = CustomerAddressAPI['endpoint'] export type CustomerAddressEndpoint = CustomerAddressAPI['endpoint']
export const handlers: CustomerAddressEndpoint['handlers'] = { export const handlers: CustomerAddressEndpoint['handlers'] = {

View File

@ -1,4 +1,4 @@
import { GetPageOperation } from "@commerce/types/page" import { GetPageOperation } from '@commerce/types/page'
export type Page = any export type Page = any
export type GetPageResult = { page?: Page } export type GetPageResult = { page?: Page }

View File

@ -14,7 +14,7 @@ export const handler: SWRHook<GetCheckoutHook> = {
}, },
useHook: ({ useData }) => useHook: ({ useData }) =>
function useHook(input) { function useHook(input) {
const submit = useSubmitCheckout(); const submit = useSubmitCheckout()
const response = useData({ const response = useData({
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
}) })

View File

@ -2,7 +2,9 @@ import type { SubmitCheckoutHook } from '@commerce/types/checkout'
import type { MutationHook } from '@commerce/utils/types' import type { MutationHook } from '@commerce/utils/types'
import { useCallback } from 'react' import { useCallback } from 'react'
import useSubmitCheckout, { UseSubmitCheckout } from '@commerce/checkout/use-submit-checkout' import useSubmitCheckout, {
UseSubmitCheckout,
} from '@commerce/checkout/use-submit-checkout'
export default useSubmitCheckout as UseSubmitCheckout<typeof handler> export default useSubmitCheckout as UseSubmitCheckout<typeof handler>

View File

@ -12,7 +12,6 @@ export const handler: SWRHook<SearchProductsHook> = {
// Use a dummy base as we only care about the relative path // Use a dummy base as we only care about the relative path
const url = new URL(options.url!, 'http://a') const url = new URL(options.url!, 'http://a')
if (search) url.searchParams.set('search', String(search)) if (search) url.searchParams.set('search', String(search))
if (categoryId) url.searchParams.set('categoryId', String(categoryId)) if (categoryId) url.searchParams.set('categoryId', String(categoryId))
if (brandId) url.searchParams.set('brandId', String(brandId)) if (brandId) url.searchParams.set('brandId', String(brandId))
@ -23,18 +22,20 @@ export const handler: SWRHook<SearchProductsHook> = {
method: options.method, method: options.method,
}) })
}, },
useHook: ({ useData }) => (input = {}) => { useHook:
return useData({ ({ useData }) =>
input: [ (input = {}) => {
['search', input.search], return useData({
['categoryId', input.categoryId], input: [
['brandId', input.brandId], ['search', input.search],
['sort', input.sort] ['categoryId', input.categoryId],
], ['brandId', input.brandId],
swrOptions: { ['sort', input.sort],
revalidateOnFocus: false, ],
...input.swrOptions, swrOptions: {
}, revalidateOnFocus: false,
}) ...input.swrOptions,
}, },
})
},
} }

View File

@ -34,7 +34,7 @@ export const ordercloudProvider = {
useCart, useCart,
useAddItem: useAddCartItem, useAddItem: useAddCartItem,
useUpdateItem: useUpdateCartItem, useUpdateItem: useUpdateCartItem,
useRemoveItem: useRemoveCartItem useRemoveItem: useRemoveCartItem,
}, },
checkout: { checkout: {
useCheckout, useCheckout,
@ -46,14 +46,14 @@ export const ordercloudProvider = {
useCards, useCards,
useAddItem: useAddCardItem, useAddItem: useAddCardItem,
useUpdateItem: useUpdateCardItem, useUpdateItem: useUpdateCardItem,
useRemoveItem: useRemoveCardItem useRemoveItem: useRemoveCardItem,
}, },
address: { address: {
useAddresses, useAddresses,
useAddItem: useAddAddressItem, useAddItem: useAddAddressItem,
useUpdateItem: useUpdateAddressItem, useUpdateItem: useUpdateAddressItem,
useRemoveItem: useRemoveAddressItem useRemoveItem: useRemoveAddressItem,
} },
}, },
products: { useSearch }, products: { useSearch },
auth: { useLogin, useLogout, useSignup }, auth: { useLogin, useLogout, useSignup },

View File

@ -1,31 +1,32 @@
import * as Core from '@commerce/types/customer/address' import * as Core from '@commerce/types/customer/address'
export type CustomerAddressTypes = Core.CustomerAddressTypes export type CustomerAddressTypes = Core.CustomerAddressTypes
export type CustomerAddressSchema = Core.CustomerAddressSchema<CustomerAddressTypes> export type CustomerAddressSchema =
Core.CustomerAddressSchema<CustomerAddressTypes>
export interface OrdercloudAddress { export interface OrdercloudAddress {
ID: string; ID: string
"FromCompanyID": string; FromCompanyID: string
"ToCompanyID": string; ToCompanyID: string
"FromUserID": string; FromUserID: string
"BillingAddressID": null, BillingAddressID: null
"BillingAddress": null, BillingAddress: null
"ShippingAddressID": null, ShippingAddressID: null
"Comments": null, Comments: null
"LineItemCount": number; LineItemCount: number
"Status": string; Status: string
"DateCreated": string; DateCreated: string
"DateSubmitted": null, DateSubmitted: null
"DateApproved": null, DateApproved: null
"DateDeclined": null, DateDeclined: null
"DateCanceled": null, DateCanceled: null
"DateCompleted": null, DateCompleted: null
"LastUpdated": string; LastUpdated: string
"Subtotal": number Subtotal: number
"ShippingCost": number ShippingCost: number
"TaxCost": number TaxCost: number
"PromotionDiscount": number PromotionDiscount: number
"Total": number Total: number
"IsSubmitted": false, IsSubmitted: false
"xp": null xp: null
} }

View File

@ -4,13 +4,13 @@ export type CustomerCardTypes = Core.CustomerCardTypes
export type CustomerCardSchema = Core.CustomerCardSchema<CustomerCardTypes> export type CustomerCardSchema = Core.CustomerCardSchema<CustomerCardTypes>
export interface OredercloudCreditCard { export interface OredercloudCreditCard {
"ID": string; ID: string
"Editable": boolean; Editable: boolean
"Token": string; Token: string
"DateCreated": string; DateCreated: string
"CardType": string; CardType: string
"PartialAccountNumber": string; PartialAccountNumber: string
"CardholderName": string; CardholderName: string
"ExpirationDate": string; ExpirationDate: string
"xp": null xp: null
} }

View File

@ -12,7 +12,7 @@ Copy the `.env.template` file in this directory to `.env.local` in the main dire
cp framework/saleor/.env.template .env.local cp framework/saleor/.env.template .env.local
``` ```
Then, set the environment following variables in your `.env.local`. Both, `NEXT_PUBLIC_SALEOR_API_URL` and `COMMERCE_IMAGE_HOST` must point to your own Saleor instance. Then, set the environment following variables in your `.env.local`. Both, `NEXT_PUBLIC_SALEOR_API_URL` and `COMMERCE_IMAGE_HOST` must point to your own Saleor instance.
``` ```
COMMERCE_PROVIDER=saleor COMMERCE_PROVIDER=saleor

View File

@ -6,14 +6,9 @@ import * as Query from '../../utils/queries'
export type Page = any export type Page = any
export type GetAllPagesResult< export type GetAllPagesResult<T extends { pages: any[] } = { pages: Page[] }> = T
T extends { pages: any[] } = { pages: Page[] }
> = T
export default function getAllPagesOperation({
commerce,
}: OperationContext<Provider>) {
export default function getAllPagesOperation({ commerce }: OperationContext<Provider>) {
async function getAllPages({ async function getAllPages({
query = Query.PageMany, query = Query.PageMany,
config, config,
@ -27,7 +22,9 @@ export default function getAllPagesOperation({
} = {}): Promise<GetAllPagesResult> { } = {}): Promise<GetAllPagesResult> {
const { fetch, locale, locales = ['en-US'] } = commerce.getConfig(config) const { fetch, locale, locales = ['en-US'] } = commerce.getConfig(config)
const { data } = await fetch(query, { variables }, const { data } = await fetch(
query,
{ variables },
{ {
...(locale && { ...(locale && {
headers: { headers: {
@ -42,7 +39,7 @@ export default function getAllPagesOperation({
url: `/${locale}/${slug}`, url: `/${locale}/${slug}`,
name, name,
})) }))
return { pages } return { pages }
} }

View File

@ -12,9 +12,7 @@ type ReturnType = {
products: Product[] products: Product[]
} }
export default function getAllProductsOperation({ export default function getAllProductsOperation({ commerce }: OperationContext<Provider>) {
commerce,
}: OperationContext<Provider>) {
async function getAllProducts({ async function getAllProducts({
query = Query.ProductMany, query = Query.ProductMany,
variables, variables,
@ -22,7 +20,7 @@ export default function getAllProductsOperation({
featured, featured,
}: { }: {
query?: string query?: string
variables?: any variables?: any
config?: Partial<SaleorConfig> config?: Partial<SaleorConfig>
preview?: boolean preview?: boolean
featured?: boolean featured?: boolean
@ -30,10 +28,9 @@ export default function getAllProductsOperation({
const { fetch, locale } = commerce.getConfig(config) const { fetch, locale } = commerce.getConfig(config)
if (featured) { if (featured) {
variables = { ...variables, categoryId: 'Q29sbGVjdGlvbjo0' }; variables = { ...variables, categoryId: 'Q29sbGVjdGlvbjo0' }
query = Query.CollectionOne query = Query.CollectionOne
} }
const { data }: GraphQLFetcherResult = await fetch( const { data }: GraphQLFetcherResult = await fetch(
query, query,
@ -48,7 +45,8 @@ export default function getAllProductsOperation({
) )
if (featured) { if (featured) {
const products = data.collection.products?.edges?.map(({ node: p }: ProductCountableEdge) => normalizeProduct(p)) ?? [] const products =
data.collection.products?.edges?.map(({ node: p }: ProductCountableEdge) => normalizeProduct(p)) ?? []
return { return {
products, products,
@ -60,7 +58,6 @@ export default function getAllProductsOperation({
products, products,
} }
} }
} }
return getAllProducts return getAllProducts

View File

@ -6,19 +6,16 @@ import * as Query from '../../utils/queries'
export type Page = any export type Page = any
export type GetPageResult<T extends { page?: any } = { page?: Page }> = T export type GetPageResult<T extends { page?: any } = { page?: Page }> = T
export default function getPageOperation({
commerce,
}: OperationContext<Provider>) {
export default function getPageOperation({ commerce }: OperationContext<Provider>) {
async function getPage({ async function getPage({
query = Query.PageOne, query = Query.PageOne,
variables, variables,
config, config,
}: { }: {
query?: string query?: string
variables: QueryPageArgs, variables: QueryPageArgs
config?: Partial<SaleorConfig> config?: Partial<SaleorConfig>
preview?: boolean preview?: boolean
}): Promise<GetPageResult> { }): Promise<GetPageResult> {
@ -26,7 +23,9 @@ export default function getPageOperation({
const { const {
data: { page }, data: { page },
} = await fetch(query, { variables }, } = await fetch(
query,
{ variables },
{ {
...(locale && { ...(locale && {
headers: { headers: {

View File

@ -1,5 +1,5 @@
import type { OperationContext } from '@commerce/api/operations' import type { OperationContext } from '@commerce/api/operations'
import { normalizeProduct, } from '../../utils' import { normalizeProduct } from '../../utils'
import type { Provider, SaleorConfig } from '..' import type { Provider, SaleorConfig } from '..'
import * as Query from '../../utils/queries' import * as Query from '../../utils/queries'
@ -12,22 +12,22 @@ type ReturnType = {
product: any product: any
} }
export default function getProductOperation({ export default function getProductOperation({ commerce }: OperationContext<Provider>) {
commerce,
}: OperationContext<Provider>) {
async function getProduct({ async function getProduct({
query = Query.ProductOneBySlug, query = Query.ProductOneBySlug,
variables, variables,
config: cfg, config: cfg,
}: { }: {
query?: string query?: string
variables: Variables variables: Variables
config?: Partial<SaleorConfig> config?: Partial<SaleorConfig>
preview?: boolean preview?: boolean
}): Promise<ReturnType> { }): Promise<ReturnType> {
const { fetch, locale } = commerce.getConfig(cfg) const { fetch, locale } = commerce.getConfig(cfg)
const { data } = await fetch(query, { variables }, const { data } = await fetch(
query,
{ variables },
{ {
...(locale && { ...(locale && {
headers: { headers: {

View File

@ -18,7 +18,7 @@ export default function getSiteInfoOperation({ commerce }: OperationContext<Prov
query?: string query?: string
config?: Partial<SaleorConfig> config?: Partial<SaleorConfig>
preview?: boolean preview?: boolean
variables?: any variables?: any
} = {}): Promise<GetSiteInfoResult> { } = {}): Promise<GetSiteInfoResult> {
const cfg = commerce.getConfig(config) const cfg = commerce.getConfig(config)

View File

@ -1,28 +1,26 @@
import type { ServerResponse } from 'http' import type { ServerResponse } from 'http'
import type { OperationContext } from '@commerce/api/operations' import type { OperationContext } from '@commerce/api/operations'
import type { Provider, SaleorConfig } from '..' import type { Provider, SaleorConfig } from '..'
import { import { throwUserErrors } from '../../utils'
throwUserErrors,
} from '../../utils'
import * as Mutation from '../../utils/mutations' import * as Mutation from '../../utils/mutations'
export default function loginOperation({ export default function loginOperation({ commerce }: OperationContext<Provider>) {
commerce,
}: OperationContext<Provider>) {
async function login({ async function login({
query = Mutation.SessionCreate, query = Mutation.SessionCreate,
variables, variables,
config, config,
}: { }: {
query?: string query?: string
variables: any variables: any
res: ServerResponse res: ServerResponse
config?: SaleorConfig config?: SaleorConfig
}): Promise<any> { }): Promise<any> {
config = commerce.getConfig(config) config = commerce.getConfig(config)
const { data: { customerAccessTokenCreate } } = await config.fetch(query, { variables }) const {
data: { customerAccessTokenCreate },
} = await config.fetch(query, { variables })
throwUserErrors(customerAccessTokenCreate?.customerUserErrors) throwUserErrors(customerAccessTokenCreate?.customerUserErrors)

View File

@ -29,7 +29,7 @@ export const handler: MutationHook<SignupHook> = {
email, email,
password, password,
redirectUrl: 'https://localhost.com', redirectUrl: 'https://localhost.com',
channel: 'default-channel' channel: 'default-channel',
}, },
}, },
}) })

View File

@ -21,9 +21,9 @@ export const handler = {
}) })
return checkoutToCart(data.checkoutLineDelete) return checkoutToCart(data.checkoutLineDelete)
}, },
useHook: ({ fetch }: MutationHookContext<RemoveItemHook>) => < useHook:
T extends LineItem | undefined = undefined ({ fetch }: MutationHookContext<RemoveItemHook>) =>
> () => { <T extends LineItem | undefined = undefined>() => {
const { mutate } = useCart() const { mutate } = useCart()
return useCallback( return useCallback(
@ -34,6 +34,6 @@ export const handler = {
return data return data
}, },
[fetch, mutate] [fetch, mutate]
); )
}, },
} }

View File

@ -23,11 +23,7 @@ export default useUpdateItem as UseUpdateItem<typeof handler>
export const handler = { export const handler = {
fetchOptions: { query: mutation.CheckoutLineUpdate }, fetchOptions: { query: mutation.CheckoutLineUpdate },
async fetcher({ async fetcher({ input: { itemId, item }, options, fetch }: HookFetcherContext<UpdateItemHook>) {
input: { itemId, item },
options,
fetch
}: HookFetcherContext<UpdateItemHook>) {
if (Number.isInteger(item.quantity)) { if (Number.isInteger(item.quantity)) {
// Also allow the update hook to remove an item if the quantity is lower than 1 // Also allow the update hook to remove an item if the quantity is lower than 1
if (item.quantity! < 1) { if (item.quantity! < 1) {
@ -59,7 +55,8 @@ export const handler = {
return checkoutToCart(checkoutLinesUpdate) return checkoutToCart(checkoutLinesUpdate)
}, },
useHook: ({ fetch }: MutationHookContext<UpdateItemHook>) => useHook:
({ fetch }: MutationHookContext<UpdateItemHook>) =>
<T extends LineItem | undefined = undefined>( <T extends LineItem | undefined = undefined>(
ctx: { ctx: {
item?: T item?: T

View File

@ -1,7 +1,15 @@
import { Cart } from '../types' import { Cart } from '../types'
import { CommerceError } from '@commerce/utils/errors' import { CommerceError } from '@commerce/utils/errors'
import { CheckoutLinesAdd, CheckoutLinesUpdate, CheckoutCreate, CheckoutError, Checkout, Maybe, CheckoutLineDelete } from '../schema' import {
CheckoutLinesAdd,
CheckoutLinesUpdate,
CheckoutCreate,
CheckoutError,
Checkout,
Maybe,
CheckoutLineDelete,
} from '../schema'
import { normalizeCart } from './normalize' import { normalizeCart } from './normalize'
import throwUserErrors from './throw-user-errors' import throwUserErrors from './throw-user-errors'
@ -11,7 +19,12 @@ export type CheckoutQuery = {
errors?: Array<CheckoutError> errors?: Array<CheckoutError>
} }
export type CheckoutPayload = CheckoutLinesAdd | CheckoutLinesUpdate | CheckoutCreate | CheckoutQuery | CheckoutLineDelete export type CheckoutPayload =
| CheckoutLinesAdd
| CheckoutLinesUpdate
| CheckoutCreate
| CheckoutQuery
| CheckoutLineDelete
const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => { const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => {
if (!checkoutPayload) { if (!checkoutPayload) {

View File

@ -38,9 +38,11 @@ export default function getAllPagesOperation({
preview?: boolean preview?: boolean
query?: string query?: string
} = {}): Promise<T['data']> { } = {}): Promise<T['data']> {
const { fetch, locale, locales = ['en-US', 'es'] } = commerce.getConfig( const {
config fetch,
) locale,
locales = ['en-US', 'es'],
} = commerce.getConfig(config)
const { data } = await fetch<GetAllPagesQuery, GetAllPagesQueryVariables>( const { data } = await fetch<GetAllPagesQuery, GetAllPagesQueryVariables>(
query, query,

View File

@ -21,8 +21,7 @@ export const handler: MutationHook<LoginHook> = {
async fetcher({ input: { email, password }, options, fetch }) { async fetcher({ input: { email, password }, options, fetch }) {
if (!(email && password)) { if (!(email && password)) {
throw new CommerceError({ throw new CommerceError({
message: message: 'An email and password are required to login',
'An email and password are required to login',
}) })
} }
@ -47,16 +46,18 @@ export const handler: MutationHook<LoginHook> = {
return null return null
}, },
useHook: ({ fetch }) => () => { useHook:
const { revalidate } = useCustomer() ({ fetch }) =>
() => {
const { revalidate } = useCustomer()
return useCallback( return useCallback(
async function login(input) { async function login(input) {
const data = await fetch({ input }) const data = await fetch({ input })
await revalidate() await revalidate()
return data return data
}, },
[fetch, revalidate] [fetch, revalidate]
) )
}, },
} }

View File

@ -22,16 +22,18 @@ export const handler: MutationHook<LogoutHook> = {
setCustomerToken(null) setCustomerToken(null)
return null return null
}, },
useHook: ({ fetch }) => () => { useHook:
const { mutate } = useCustomer() ({ fetch }) =>
() => {
const { mutate } = useCustomer()
return useCallback( return useCallback(
async function logout() { async function logout() {
const data = await fetch() const data = await fetch()
await mutate(null, false) await mutate(null, false)
return data return data
}, },
[fetch, mutate] [fetch, mutate]
) )
}, },
} }

View File

@ -50,16 +50,18 @@ export const handler: MutationHook<SignupHook> = {
return null return null
}, },
useHook: ({ fetch }) => () => { useHook:
const { revalidate } = useCustomer() ({ fetch }) =>
() => {
const { revalidate } = useCustomer()
return useCallback( return useCallback(
async function signup(input) { async function signup(input) {
const data = await fetch({ input }) const data = await fetch({ input })
await revalidate() await revalidate()
return data return data
}, },
[fetch, revalidate] [fetch, revalidate]
) )
}, },
} }

View File

@ -41,27 +41,25 @@ export const handler = {
}) })
return checkoutToCart(data.checkoutLineItemsRemove) return checkoutToCart(data.checkoutLineItemsRemove)
}, },
useHook: ({ fetch }: MutationHookContext<RemoveItemHook>) => < useHook:
T extends LineItem | undefined = undefined ({ fetch }: MutationHookContext<RemoveItemHook>) =>
>( <T extends LineItem | undefined = undefined>(ctx: { item?: T } = {}) => {
ctx: { item?: T } = {} const { item } = ctx
) => { const { mutate } = useCart()
const { item } = ctx const removeItem: RemoveItemFn<LineItem> = async (input) => {
const { mutate } = useCart() const itemId = input?.id ?? item?.id
const removeItem: RemoveItemFn<LineItem> = async (input) => {
const itemId = input?.id ?? item?.id
if (!itemId) { if (!itemId) {
throw new ValidationError({ throw new ValidationError({
message: 'Invalid input used for this operation', message: 'Invalid input used for this operation',
}) })
}
const data = await fetch({ input: { itemId } })
await mutate(data, false)
return data
} }
const data = await fetch({ input: { itemId } }) return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate])
await mutate(data, false) },
return data
}
return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate])
},
} }

View File

@ -64,42 +64,42 @@ export const handler = {
return checkoutToCart(checkoutLineItemsUpdate) return checkoutToCart(checkoutLineItemsUpdate)
}, },
useHook: ({ fetch }: MutationHookContext<UpdateItemHook>) => < useHook:
T extends LineItem | undefined = undefined ({ fetch }: MutationHookContext<UpdateItemHook>) =>
>( <T extends LineItem | undefined = undefined>(
ctx: { ctx: {
item?: T item?: T
wait?: number wait?: number
} = {} } = {}
) => { ) => {
const { item } = ctx const { item } = ctx
const { mutate } = useCart() as any const { mutate } = useCart() as any
return useCallback( return useCallback(
debounce(async (input: UpdateItemActionInput<T>) => { debounce(async (input: UpdateItemActionInput<T>) => {
const itemId = input.id ?? item?.id const itemId = input.id ?? item?.id
const productId = input.productId ?? item?.productId const productId = input.productId ?? item?.productId
const variantId = input.productId ?? item?.variantId const variantId = input.productId ?? item?.variantId
if (!itemId || !productId || !variantId) { if (!itemId || !productId || !variantId) {
throw new ValidationError({ throw new ValidationError({
message: 'Invalid input used for this operation', message: 'Invalid input used for this operation',
}) })
} }
const data = await fetch({ const data = await fetch({
input: { input: {
item: { item: {
productId, productId,
variantId, variantId,
quantity: input.quantity, quantity: input.quantity,
},
itemId,
}, },
itemId, })
}, await mutate(data, false)
}) return data
await mutate(data, false) }, ctx.wait ?? 500),
return data [fetch, mutate]
}, ctx.wait ?? 500), )
[fetch, mutate] },
)
},
} }

View File

@ -21,12 +21,14 @@ export const handler: SWRHook<CustomerHook> = {
} }
return null return null
}, },
useHook: ({ useData }) => (input) => { useHook:
return useData({ ({ useData }) =>
swrOptions: { (input) => {
revalidateOnFocus: false, return useData({
...input?.swrOptions, swrOptions: {
}, revalidateOnFocus: false,
}) ...input?.swrOptions,
}, },
})
},
} }

View File

@ -71,19 +71,21 @@ export const handler: SWRHook<SearchProductsHook> = {
found: !!products?.length, found: !!products?.length,
} }
}, },
useHook: ({ useData }) => (input = {}) => { useHook:
return useData({ ({ useData }) =>
input: [ (input = {}) => {
['search', input.search], return useData({
['categoryId', input.categoryId], input: [
['brandId', input.brandId], ['search', input.search],
['sort', input.sort], ['categoryId', input.categoryId],
['locale', input.locale], ['brandId', input.brandId],
], ['sort', input.sort],
swrOptions: { ['locale', input.locale],
revalidateOnFocus: false, ],
...input.swrOptions, swrOptions: {
}, revalidateOnFocus: false,
}) ...input.swrOptions,
}, },
})
},
} }

View File

@ -2,9 +2,8 @@ import { Provider, SwellConfig } from '..'
import type { OperationContext } from '@commerce/api/operations' import type { OperationContext } from '@commerce/api/operations'
import type { Page } from '../../types/page' import type { Page } from '../../types/page'
export type GetAllPagesResult< export type GetAllPagesResult<T extends { pages: any[] } = { pages: Page[] }> =
T extends { pages: any[] } = { pages: Page[] } T
> = T
export default function getAllPagesOperation({ export default function getAllPagesOperation({
commerce, commerce,

View File

@ -59,16 +59,18 @@ export const handler: MutationHook<LoginHook> = {
return null return null
}, },
useHook: ({ fetch }) => () => { useHook:
const { revalidate } = useCustomer() ({ fetch }) =>
() => {
const { revalidate } = useCustomer()
return useCallback( return useCallback(
async function login(input) { async function login(input) {
const data = await fetch({ input }) const data = await fetch({ input })
await revalidate() await revalidate()
return data return data
}, },
[fetch, revalidate] [fetch, revalidate]
) )
}, },
} }

View File

@ -22,16 +22,18 @@ export const handler: MutationHook<LogoutHook> = {
setCustomerToken(null) setCustomerToken(null)
return null return null
}, },
useHook: ({ fetch }) => () => { useHook:
const { mutate } = useCustomer() ({ fetch }) =>
() => {
const { mutate } = useCustomer()
return useCallback( return useCallback(
async function logout() { async function logout() {
const data = await fetch() const data = await fetch()
await mutate(null, false) await mutate(null, false)
return data return data
}, },
[fetch, mutate] [fetch, mutate]
) )
}, },
} }

View File

@ -44,16 +44,18 @@ export const handler: MutationHook<SignupHook> = {
} catch (error) {} } catch (error) {}
return data return data
}, },
useHook: ({ fetch }) => () => { useHook:
const { revalidate } = useCustomer() ({ fetch }) =>
() => {
const { revalidate } = useCustomer()
return useCallback( return useCallback(
async function signup(input) { async function signup(input) {
const data = await fetch({ input }) const data = await fetch({ input })
await revalidate() await revalidate()
return data return data
}, },
[fetch, revalidate] [fetch, revalidate]
) )
}, },
} }

View File

@ -44,16 +44,18 @@ export const handler: MutationHook<AddItemHook> = {
return checkoutToCart(response) as any return checkoutToCart(response) as any
}, },
useHook: ({ fetch }) => () => { useHook:
const { mutate } = useCart() ({ fetch }) =>
() => {
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 })
await mutate(data, false) await mutate(data, false)
return data return data
}, },
[fetch, mutate] [fetch, mutate]
) )
}, },
} }

View File

@ -17,21 +17,23 @@ export const handler: SWRHook<GetCartHook> = {
return cart ? normalizeCart(cart) : null return cart ? normalizeCart(cart) : null
}, },
useHook: ({ useData }) => (input) => { useHook:
const response = useData({ ({ useData }) =>
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, (input) => {
}) const response = useData({
return useMemo( swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
() => })
Object.create(response, { return useMemo(
isEmpty: { () =>
get() { Object.create(response, {
return (response.data?.lineItems.length ?? 0) <= 0 isEmpty: {
get() {
return (response.data?.lineItems.length ?? 0) <= 0
},
enumerable: true,
}, },
enumerable: true, }),
}, [response]
}), )
[response] },
)
},
} }

View File

@ -33,17 +33,19 @@ export const handler = {
return checkoutToCart(response) return checkoutToCart(response)
}, },
useHook: ({ fetch }: MutationHookContext<RemoveItemHook>) => () => { useHook:
const { mutate } = useCart() ({ fetch }: MutationHookContext<RemoveItemHook>) =>
() => {
const { mutate } = useCart()
return useCallback( return useCallback(
async function removeItem(input) { async function removeItem(input) {
const data = await fetch({ input: { itemId: input.id } }) const data = await fetch({ input: { itemId: input.id } })
await mutate(data, false) await mutate(data, false)
return data return data
}, },
[fetch, mutate] [fetch, mutate]
) )
}, },
} }

View File

@ -57,43 +57,43 @@ export const handler = {
return checkoutToCart(response) return checkoutToCart(response)
}, },
useHook: ({ fetch }: MutationHookContext<UpdateItemHook>) => < useHook:
T extends LineItem | undefined = undefined ({ fetch }: MutationHookContext<UpdateItemHook>) =>
>( <T extends LineItem | undefined = undefined>(
ctx: { ctx: {
item?: T item?: T
wait?: number wait?: number
} = {} } = {}
) => { ) => {
const { item } = ctx const { item } = ctx
const { mutate, data: cartData } = useCart() as any const { mutate, data: cartData } = useCart() as any
return useCallback( return useCallback(
debounce(async (input: UpdateItemActionInput) => { debounce(async (input: UpdateItemActionInput) => {
const firstLineItem = cartData.lineItems[0] const firstLineItem = cartData.lineItems[0]
const itemId = item?.id || firstLineItem.id const itemId = item?.id || firstLineItem.id
const productId = item?.productId || firstLineItem.productId const productId = item?.productId || firstLineItem.productId
const variantId = item?.variant.id || firstLineItem.variant.id const variantId = item?.variant.id || firstLineItem.variant.id
if (!itemId || !productId) { if (!itemId || !productId) {
throw new ValidationError({ throw new ValidationError({
message: 'Invalid input used for this operation', message: 'Invalid input used for this operation',
}) })
} }
const data = await fetch({ const data = await fetch({
input: { input: {
item: { item: {
productId, productId,
variantId, variantId,
quantity: input.quantity, quantity: input.quantity,
},
itemId,
}, },
itemId, })
}, await mutate(data, false)
}) return data
await mutate(data, false) }, ctx.wait ?? 500),
return data [fetch, mutate]
}, ctx.wait ?? 500), )
[fetch, mutate] },
)
},
} }

View File

@ -16,12 +16,14 @@ export const handler: SWRHook<CustomerHook> = {
}) })
return data ? normalizeCustomer(data) : null return data ? normalizeCustomer(data) : null
}, },
useHook: ({ useData }) => (input) => { useHook:
return useData({ ({ useData }) =>
swrOptions: { (input) => {
revalidateOnFocus: false, return useData({
...input?.swrOptions, swrOptions: {
}, revalidateOnFocus: false,
}) ...input?.swrOptions,
}, },
})
},
} }

View File

@ -42,18 +42,20 @@ export const handler: SWRHook<SearchProductsHook> = {
found, found,
} }
}, },
useHook: ({ useData }) => (input = {}) => { useHook:
return useData({ ({ useData }) =>
input: [ (input = {}) => {
['search', input.search], return useData({
['categoryId', input.categoryId], input: [
['brandId', input.brandId], ['search', input.search],
['sort', input.sort], ['categoryId', input.categoryId],
], ['brandId', input.brandId],
swrOptions: { ['sort', input.sort],
revalidateOnFocus: false, ],
...input.swrOptions, swrOptions: {
}, revalidateOnFocus: false,
}) ...input.swrOptions,
}, },
})
},
} }

View File

@ -4,9 +4,8 @@ import { Provider } from '../../../bigcommerce/api'
export type Page = any export type Page = any
export type GetAllPagesResult< export type GetAllPagesResult<T extends { pages: any[] } = { pages: Page[] }> =
T extends { pages: any[] } = { pages: Page[] } T
> = T
export default function getAllPagesOperation({ export default function getAllPagesOperation({
commerce, commerce,

View File

@ -36,16 +36,18 @@ export const handler: MutationHook<LoginHook> = {
return null return null
}, },
useHook: ({ fetch }) => () => { useHook:
const { revalidate } = useCustomer() ({ fetch }) =>
() => {
const { revalidate } = useCustomer()
return useCallback( return useCallback(
async function login(input) { async function login(input) {
const data = await fetch({ input }) const data = await fetch({ input })
await revalidate() await revalidate()
return data return data
}, },
[fetch, revalidate] [fetch, revalidate]
) )
}, },
} }

View File

@ -54,16 +54,18 @@ export const handler: MutationHook<SignupHook> = {
return null return null
}, },
useHook: ({ fetch }) => () => { useHook:
const { revalidate } = useCustomer() ({ fetch }) =>
() => {
const { revalidate } = useCustomer()
return useCallback( return useCallback(
async function signup(input) { async function signup(input) {
const data = await fetch({ input }) const data = await fetch({ input })
await revalidate() await revalidate()
return data return data
}, },
[fetch, revalidate] [fetch, revalidate]
) )
}, },
} }

View File

@ -37,16 +37,18 @@ export const handler: MutationHook<AddItemHook> = {
} }
throw new CommerceError(addItemToOrder) throw new CommerceError(addItemToOrder)
}, },
useHook: ({ fetch }) => () => { useHook:
const { mutate } = useCart() ({ fetch }) =>
() => {
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 })
await mutate(data, false) await mutate(data, false)
return data return data
}, },
[fetch, mutate] [fetch, mutate]
) )
}, },
} }

View File

@ -23,22 +23,24 @@ export const handler: SWRHook<GetCartHook> = {
const { activeOrder } = await fetch<ActiveOrderQuery>(options) const { activeOrder } = await fetch<ActiveOrderQuery>(options)
return activeOrder ? normalizeCart(activeOrder) : null return activeOrder ? normalizeCart(activeOrder) : null
}, },
useHook: ({ useData }) => (input) => { useHook:
const response = useData({ ({ useData }) =>
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, (input) => {
}) const response = useData({
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
})
return useMemo( return useMemo(
() => () =>
Object.create(response, { Object.create(response, {
isEmpty: { isEmpty: {
get() { get() {
return (response.data?.lineItems.length ?? 0) <= 0 return (response.data?.lineItems.length ?? 0) <= 0
},
enumerable: true,
}, },
enumerable: true, }),
}, [response]
}), )
[response] },
)
},
} }

View File

@ -37,16 +37,18 @@ export const handler: MutationHook<RemoveItemHook> = {
} }
throw new CommerceError(removeOrderLine) throw new CommerceError(removeOrderLine)
}, },
useHook: ({ fetch }) => () => { useHook:
const { mutate } = useCart() ({ fetch }) =>
() => {
const { mutate } = useCart()
return useCallback( return useCallback(
async function removeItem(input) { async function removeItem(input) {
const data = await fetch({ input: { itemId: input.id } }) const data = await fetch({ input: { itemId: input.id } })
await mutate(data, false) await mutate(data, false)
return data return data
}, },
[fetch, mutate] [fetch, mutate]
) )
}, },
} }

View File

@ -42,39 +42,41 @@ export const handler = {
} }
throw new CommerceError(adjustOrderLine) throw new CommerceError(adjustOrderLine)
}, },
useHook: ({ fetch }: MutationHookContext<UpdateItemHook>) => ( useHook:
ctx: { ({ fetch }: MutationHookContext<UpdateItemHook>) =>
item?: LineItem (
wait?: number ctx: {
} = {} item?: LineItem
) => { wait?: number
const { item } = ctx } = {}
const { mutate } = useCart() ) => {
const { item } = ctx
const { mutate } = useCart()
return useCallback( return useCallback(
async function addItem(input: UpdateItemActionInput) { async function addItem(input: UpdateItemActionInput) {
const itemId = item?.id const itemId = item?.id
const productId = input.productId ?? item?.productId const productId = input.productId ?? item?.productId
const variantId = input.productId ?? item?.variantId const variantId = input.productId ?? item?.variantId
if (!itemId || !productId || !variantId) { if (!itemId || !productId || !variantId) {
throw new ValidationError({ throw new ValidationError({
message: 'Invalid input used for this operation', message: 'Invalid input used for this operation',
}) })
} }
const data = await fetch({ const data = await fetch({
input: { input: {
item: { item: {
productId, productId,
variantId, variantId,
quantity: input.quantity, quantity: input.quantity,
},
itemId,
}, },
itemId, })
}, await mutate(data, false)
}) return data
await mutate(data, false) },
return data [fetch, mutate]
}, )
[fetch, mutate] },
)
},
} }

View File

@ -22,12 +22,14 @@ export const handler: SWRHook<CustomerHook> = {
} as any) } as any)
: null : null
}, },
useHook: ({ useData }) => (input) => { useHook:
return useData({ ({ useData }) =>
swrOptions: { (input) => {
revalidateOnFocus: false, return useData({
...input?.swrOptions, swrOptions: {
}, revalidateOnFocus: false,
}) ...input?.swrOptions,
}, },
})
},
} }

View File

@ -48,6 +48,6 @@ export const fetcher: Fetcher = async ({
} }
return data return data
} }
throw await getError(res) throw await getError(res)
} }

View File

@ -45,18 +45,20 @@ export const handler: SWRHook<SearchProductsHook> = {
products: search.items.map((item) => normalizeSearchResult(item)) ?? [], products: search.items.map((item) => normalizeSearchResult(item)) ?? [],
} }
}, },
useHook: ({ useData }) => (input = {}) => { useHook:
return useData({ ({ useData }) =>
input: [ (input = {}) => {
['search', input.search], return useData({
['categoryId', input.categoryId], input: [
['brandId', input.brandId], ['search', input.search],
['sort', input.sort], ['categoryId', input.categoryId],
], ['brandId', input.brandId],
swrOptions: { ['sort', input.sort],
revalidateOnFocus: false, ],
...input.swrOptions, swrOptions: {
}, revalidateOnFocus: false,
}) ...input.swrOptions,
}, },
})
},
} }