4
0
forked from crowetic/commerce

Update Swell Provider (#359)

* fix update cart item

* update types

* update checkout

* update get-page, cleanup types

* revert change to incorrect file

* cleanup

Co-authored-by: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local>
This commit is contained in:
ghoskin 2021-06-14 13:37:18 -07:00 committed by GitHub
parent ddd4631ade
commit 4d85b43a30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 503 additions and 545 deletions

View File

@ -1,20 +0,0 @@
import createApiHandler, { SwellApiHandler } from '../utils/create-api-handler'
import { SWELL_CHECKOUT_URL_COOKIE } from '../../const'
import { getConfig } from '..'
const checkoutApi: SwellApiHandler<any> = async (req, res, config) => {
config = getConfig()
const { cookies } = req
const checkoutUrl = cookies[SWELL_CHECKOUT_URL_COOKIE]
if (checkoutUrl) {
res.redirect(checkoutUrl)
} else {
res.redirect('/cart')
}
}
export default createApiHandler(checkoutApi, {}, {})

View File

@ -1 +0,0 @@
export default function () {}

View File

@ -0,0 +1 @@
export default function (_commerce: any) {}

View File

@ -0,0 +1 @@
export default function (_commerce: any) {}

View File

@ -0,0 +1,30 @@
import { CommerceAPI, createEndpoint, GetAPISchema } from '@commerce/api'
import { CheckoutSchema } from '@commerce/types/checkout'
import { SWELL_CHECKOUT_URL_COOKIE } from '../../../const'
import checkoutEndpoint from '@commerce/api/endpoints/checkout'
const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({
req,
res,
config,
}) => {
const { cookies } = req
const checkoutUrl = cookies[SWELL_CHECKOUT_URL_COOKIE]
if (checkoutUrl) {
res.redirect(checkoutUrl)
} else {
res.redirect('/cart')
}
}
export const handlers: CheckoutEndpoint['handlers'] = { checkout }
export type CheckoutAPI = GetAPISchema<CommerceAPI, CheckoutSchema>
export type CheckoutEndpoint = CheckoutAPI['endpoint']
const checkoutApi = createEndpoint<CheckoutAPI>({
handler: checkoutEndpoint,
handlers,
})
export default checkoutApi

View File

@ -0,0 +1 @@
export default function (_commerce: any) {}

View File

@ -0,0 +1 @@
export default function (_commerce: any) {}

View File

@ -0,0 +1 @@
export default function (_commerce: any) {}

View File

@ -0,0 +1 @@
export default function (_commerce: any) {}

View File

@ -0,0 +1 @@
export default function (_commerce: any) {}

View File

@ -1,5 +1,8 @@
import type { CommerceAPIConfig } from '@commerce/api'
import {
CommerceAPI,
CommerceAPIConfig,
getCommerceApi as commerceApi,
} from '@commerce/api'
import {
SWELL_CHECKOUT_ID_COOKIE,
SWELL_CUSTOMER_TOKEN_COOKIE,
@ -7,31 +10,19 @@ import {
} from '../const'
import fetchApi from './utils/fetch-swell-api'
import login from './operations/login'
import getAllPages from './operations/get-all-pages'
import getPage from './operations/get-page'
import getSiteInfo from './operations/get-site-info'
import getAllProductPaths from './operations/get-all-product-paths'
import getAllProducts from './operations/get-all-products'
import getProduct from './operations/get-product'
export interface SwellConfig extends CommerceAPIConfig {
fetch: any
}
export class Config {
private config: SwellConfig
constructor(config: SwellConfig) {
this.config = config
}
getConfig(userConfig: Partial<SwellConfig> = {}) {
return Object.entries(userConfig).reduce<SwellConfig>(
(cfg, [key, value]) => Object.assign(cfg, { [key]: value }),
{ ...this.config }
)
}
setConfig(newConfig: Partial<SwellConfig>) {
Object.assign(this.config, newConfig)
}
}
const config = new Config({
const config: SwellConfig = {
locale: 'en-US',
commerceUrl: '',
apiToken: ''!,
@ -39,12 +30,24 @@ const config = new Config({
cartCookieMaxAge: SWELL_COOKIE_EXPIRE,
fetch: fetchApi,
customerCookie: SWELL_CUSTOMER_TOKEN_COOKIE,
})
export function getConfig(userConfig?: Partial<SwellConfig>) {
return config.getConfig(userConfig)
}
export function setConfig(newConfig: Partial<SwellConfig>) {
return config.setConfig(newConfig)
const operations = {
login,
getAllPages,
getPage,
getSiteInfo,
getAllProductPaths,
getAllProducts,
getProduct,
}
export const provider = { config, operations }
export type Provider = typeof provider
export function getCommerceApi<P extends Provider>(
customProvider: P = provider as any
): CommerceAPI<P> {
return commerceApi(customProvider)
}

View File

@ -0,0 +1,45 @@
import { Provider, SwellConfig } from '..'
import type { OperationContext } from '@commerce/api/operations'
import type { Page } from '../../types/page'
export type GetAllPagesResult<
T extends { pages: any[] } = { pages: Page[] }
> = T
export default function getAllPagesOperation({
commerce,
}: OperationContext<Provider>) {
async function getAllPages(opts?: {
config?: Partial<SwellConfig>
preview?: boolean
}): Promise<GetAllPagesResult>
async function getAllPages<T extends { pages: any[] }>(opts: {
url: string
config?: Partial<SwellConfig>
preview?: boolean
}): Promise<GetAllPagesResult<T>>
async function getAllPages({
config: cfg,
preview,
}: {
url?: string
config?: Partial<SwellConfig>
preview?: boolean
} = {}): Promise<GetAllPagesResult> {
const config = commerce.getConfig(cfg)
const { locale, fetch } = config
const data = await fetch('content', 'list', ['pages'])
const pages =
data?.results?.map(({ slug, ...rest }: { slug: string }) => ({
url: `/${locale}/${slug}`,
...rest,
})) ?? []
return {
pages,
}
}
return getAllPages
}

View File

@ -0,0 +1,48 @@
import { SwellProduct } from '../../types'
import { SwellConfig, Provider } from '..'
import { OperationContext, OperationOptions } from '@commerce/api/operations'
import { GetAllProductPathsOperation } from '@commerce/types/product'
export default function getAllProductPathsOperation({
commerce,
}: OperationContext<Provider>) {
async function getAllProductPaths<
T extends GetAllProductPathsOperation
>(opts?: {
variables?: T['variables']
config?: SwellConfig
}): Promise<T['data']>
async function getAllProductPaths<T extends GetAllProductPathsOperation>(
opts: {
variables?: T['variables']
config?: SwellConfig
} & OperationOptions
): Promise<T['data']>
async function getAllProductPaths<T extends GetAllProductPathsOperation>({
variables,
config: cfg,
}: {
query?: string
variables?: T['variables']
config?: SwellConfig
} = {}): Promise<T['data']> {
const config = commerce.getConfig(cfg)
// RecursivePartial forces the method to check for every prop in the data, which is
// required in case there's a custom `query`
const { results } = await config.fetch('products', 'list', [
{
limit: variables?.first,
},
])
return {
products: results?.map(({ slug: handle }: SwellProduct) => ({
path: `/${handle}`,
})),
}
}
return getAllProductPaths
}

View File

@ -0,0 +1,43 @@
import { normalizeProduct } from '../../utils/normalize'
import { SwellProduct } from '../../types'
import { Product } from '@commerce/types/product'
import { Provider, SwellConfig } from '../'
import { OperationContext } from '@commerce/api/operations'
export type ProductVariables = { first?: number }
export default function getAllProductsOperation({
commerce,
}: OperationContext<Provider>) {
async function getAllProducts(opts?: {
variables?: ProductVariables
config?: Partial<SwellConfig>
preview?: boolean
}): Promise<{ products: Product[] }>
async function getAllProducts({
config: cfg,
variables = { first: 250 },
}: {
query?: string
variables?: ProductVariables
config?: Partial<SwellConfig>
preview?: boolean
} = {}): Promise<{ products: Product[] | any[] }> {
const config = commerce.getConfig(cfg)
const { results } = await config.fetch('products', 'list', [
{
limit: variables.first,
},
])
const products = results.map((product: SwellProduct) =>
normalizeProduct(product)
)
return {
products,
}
}
return getAllProducts
}

View File

@ -1,25 +1,54 @@
import { Page } from '../../schema'
import { SwellConfig, getConfig } from '..'
import { SwellConfig, Provider } from '..'
import { OperationContext, OperationOptions } from '@commerce/api/operations'
import { GetPageOperation } from '../../types/page'
export type GetPageResult<T extends { page?: any } = { page?: Page }> = T
export type PageVariables = {
id: string
id: number
}
async function getPage({
url,
variables,
config,
preview,
}: {
url?: string
variables: PageVariables
config?: SwellConfig
preview?: boolean
}): Promise<GetPageResult> {
config = getConfig(config)
return {}
}
export default function getPageOperation({
commerce,
}: OperationContext<Provider>) {
async function getPage<T extends GetPageOperation>(opts: {
variables: T['variables']
config?: Partial<SwellConfig>
preview?: boolean
}): Promise<T['data']>
export default getPage
async function getPage<T extends GetPageOperation>(
opts: {
variables: T['variables']
config?: Partial<SwellConfig>
preview?: boolean
} & OperationOptions
): Promise<T['data']>
async function getPage<T extends GetPageOperation>({
variables,
config,
}: {
query?: string
variables: T['variables']
config?: Partial<SwellConfig>
preview?: boolean
}): Promise<T['data']> {
const { fetch, locale = 'en-US' } = commerce.getConfig(config)
const id = variables.id
const result = await fetch('content', 'get', ['pages', id])
const page = result
return {
page: page
? {
...page,
url: `/${locale}/${page.slug}`,
}
: null,
}
}
return getPage
}

View File

@ -0,0 +1,33 @@
import { normalizeProduct } from '../../utils'
import { Product } from '@commerce/types/product'
import { OperationContext } from '@commerce/api/operations'
import { Provider, SwellConfig } from '../'
export default function getProductOperation({
commerce,
}: OperationContext<Provider>) {
async function getProduct({
variables,
config: cfg,
}: {
query?: string
variables: { slug: string }
config?: Partial<SwellConfig>
preview?: boolean
}): Promise<Product | {} | any> {
const config = commerce.getConfig(cfg)
const product = await config.fetch('products', 'get', [variables.slug])
if (product && product.variants) {
product.variants = product.variants?.results
}
return {
product: product ? normalizeProduct(product) : null,
}
}
return getProduct
}

View File

@ -0,0 +1,37 @@
import getCategories from '../../utils/get-categories'
import getVendors, { Brands } from '../../utils/get-vendors'
import { Provider, SwellConfig } from '../'
import { OperationContext } from '@commerce/api/operations'
import { Category } from '@commerce/types/site'
export type GetSiteInfoResult<
T extends { categories: any[]; brands: any[] } = {
categories: Category[]
brands: Brands
}
> = T
export default function getSiteInfoOperation({
commerce,
}: OperationContext<Provider>) {
async function getSiteInfo({
variables,
config: cfg,
}: {
query?: string
variables?: any
config?: Partial<SwellConfig>
preview?: boolean
} = {}): Promise<GetSiteInfoResult> {
const config = commerce.getConfig(cfg)
const categories = await getCategories(config)
const brands = await getVendors(config)
return {
categories,
brands,
}
}
return getSiteInfo
}

View File

@ -0,0 +1,46 @@
import type { ServerResponse } from 'http'
import type {
OperationContext,
OperationOptions,
} from '@commerce/api/operations'
import type { LoginOperation } from '../../types/login'
import { Provider, SwellConfig } from '..'
export default function loginOperation({
commerce,
}: OperationContext<Provider>) {
async function login<T extends LoginOperation>(opts: {
variables: T['variables']
config?: Partial<SwellConfig>
res: ServerResponse
}): Promise<T['data']>
async function login<T extends LoginOperation>(
opts: {
variables: T['variables']
config?: Partial<SwellConfig>
res: ServerResponse
} & OperationOptions
): Promise<T['data']>
async function login<T extends LoginOperation>({
variables,
res: response,
config: cfg,
}: {
query?: string
variables: T['variables']
res: ServerResponse
config?: Partial<SwellConfig>
}): Promise<T['data']> {
const config = commerce.getConfig(cfg)
const { data } = await config.fetch('account', 'login', [variables])
return {
result: data,
}
}
return login
}

View File

@ -1,58 +0,0 @@
import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
import { SwellConfig, getConfig } from '..'
export type SwellApiHandler<
T = any,
H extends SwellHandlers = {},
Options extends {} = {}
> = (
req: NextApiRequest,
res: NextApiResponse<SwellApiResponse<T>>,
config: SwellConfig,
handlers: H,
// Custom configs that may be used by a particular handler
options: Options
) => void | Promise<void>
export type SwellHandler<T = any, Body = null> = (options: {
req: NextApiRequest
res: NextApiResponse<SwellApiResponse<T>>
config: SwellConfig
body: Body
}) => void | Promise<void>
export type SwellHandlers<T = any> = {
[k: string]: SwellHandler<T, any>
}
export type SwellApiResponse<T> = {
data: T | null
errors?: { message: string; code?: string }[]
}
export default function createApiHandler<
T = any,
H extends SwellHandlers = {},
Options extends {} = {}
>(
handler: SwellApiHandler<T, H, Options>,
handlers: H,
defaultOptions: Options
) {
return function getApiHandler({
config,
operations,
options,
}: {
config?: SwellConfig
operations?: Partial<H>
options?: Options extends {} ? Partial<Options> : never
} = {}): NextApiHandler {
const ops = { ...operations, ...handlers }
const opts = { ...defaultOptions, ...options }
return function apiHandler(req, res) {
return handler(req, res, getConfig(config), ops, opts)
}
}
}

View File

@ -3,12 +3,12 @@ import type { MutationHook } from '@commerce/utils/types'
import { CommerceError, ValidationError } from '@commerce/utils/errors'
import useCustomer from '../customer/use-customer'
import {
CustomerAccessTokenCreateInput,
CustomerUserError,
Mutation,
MutationCheckoutCreateArgs,
} from '../schema'
import useLogin, { UseLogin } from '@commerce/auth/use-login'
import { LoginHook } from '../types/login'
import { setCustomerToken } from '../utils'
export default useLogin as UseLogin<typeof handler>
@ -22,7 +22,7 @@ const getErrorMessage = ({ code, message }: CustomerUserError) => {
return message
}
export const handler: MutationHook<null, {}, CustomerAccessTokenCreateInput> = {
export const handler: MutationHook<LoginHook> = {
fetchOptions: {
query: 'account',
method: 'login',

View File

@ -3,10 +3,11 @@ import type { MutationHook } from '@commerce/utils/types'
import useLogout, { UseLogout } from '@commerce/auth/use-logout'
import useCustomer from '../customer/use-customer'
import { getCustomerToken, setCustomerToken } from '../utils/customer-token'
import { LogoutHook } from '../types/logout'
export default useLogout as UseLogout<typeof handler>
export const handler: MutationHook<null> = {
export const handler: MutationHook<LogoutHook> = {
fetchOptions: {
query: 'account',
method: 'logout',

View File

@ -3,18 +3,12 @@ import type { MutationHook } from '@commerce/utils/types'
import { CommerceError } from '@commerce/utils/errors'
import useSignup, { UseSignup } from '@commerce/auth/use-signup'
import useCustomer from '../customer/use-customer'
import { CustomerCreateInput } from '../schema'
import { SignupHook } from '../types/signup'
import handleLogin from '../utils/handle-login'
export default useSignup as UseSignup<typeof handler>
export const handler: MutationHook<
null,
{},
CustomerCreateInput,
CustomerCreateInput
> = {
export const handler: MutationHook<SignupHook> = {
fetchOptions: {
query: 'account',
method: 'create',

View File

@ -2,14 +2,14 @@ import type { MutationHook } from '@commerce/utils/types'
import { CommerceError } from '@commerce/utils/errors'
import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item'
import useCart from './use-cart'
import { Cart, CartItemBody } from '../types'
import { checkoutToCart } from './utils'
import { getCheckoutId } from '../utils'
import { useCallback } from 'react'
import { AddItemHook } from '../types/cart'
export default useAddItem as UseAddItem<typeof handler>
export const handler: MutationHook<Cart, {}, CartItemBody> = {
export const handler: MutationHook<AddItemHook> = {
fetchOptions: {
query: 'cart',
method: 'addItem',
@ -24,7 +24,7 @@ export const handler: MutationHook<Cart, {}, CartItemBody> = {
})
}
const variables: {
product_id: string
product_id: string | undefined
variant_id?: string
checkoutId?: string
quantity?: number

View File

@ -1,13 +1,13 @@
import useCart, { UseCart } from '@commerce/cart/use-cart'
import { Cart } from '@commerce/types'
import { SWRHook } from '@commerce/utils/types'
import { useMemo } from 'react'
import { normalizeCart } from '../utils/normalize'
import { checkoutCreate, checkoutToCart } from './utils'
import type { GetCartHook } from '@commerce/types/cart'
export default useCart as UseCart<typeof handler>
export const handler: SWRHook<Cart | null, {}, any, { isEmpty?: boolean }> = {
export const handler: SWRHook<GetCartHook> = {
fetchOptions: {
query: 'cart',
method: 'get',

View File

@ -1,29 +1,21 @@
import { useCallback } from 'react'
import type {
MutationHookContext,
HookFetcherContext,
} from '@commerce/utils/types'
import { ValidationError } from '@commerce/utils/errors'
import useRemoveItem, {
RemoveItemInput as RemoveItemInputBase,
UseRemoveItem,
} from '@commerce/cart/use-remove-item'
import useRemoveItem, { UseRemoveItem } from '@commerce/cart/use-remove-item'
import type { Cart, LineItem, RemoveItemHook } from '@commerce/types/cart'
import useCart from './use-cart'
import { checkoutToCart } from './utils'
import { Cart, LineItem } from '../types'
import { RemoveCartItemBody } from '@commerce/types'
export type RemoveItemFn<T = any> = T extends LineItem
? (input?: RemoveItemInput<T>) => Promise<Cart | null>
: (input: RemoveItemInput<T>) => Promise<Cart | null>
? (input?: RemoveItemActionInput<T>) => Promise<Cart | null | undefined>
: (input: RemoveItemActionInput<T>) => Promise<Cart | null>
export type RemoveItemInput<T = any> = T extends LineItem
? Partial<RemoveItemInputBase>
: RemoveItemInputBase
export type RemoveItemActionInput<T = any> = T extends LineItem
? Partial<RemoveItemHook['actionInput']>
: RemoveItemHook['actionInput']
export default useRemoveItem as UseRemoveItem<typeof handler>
@ -36,36 +28,22 @@ export const handler = {
input: { itemId },
options,
fetch,
}: HookFetcherContext<RemoveCartItemBody>) {
const response = await fetch({
...options,
variables: [itemId],
})
}: HookFetcherContext<RemoveItemHook>) {
const response = await fetch({ ...options, variables: [itemId] })
return checkoutToCart(response)
},
useHook: ({
fetch,
}: MutationHookContext<Cart | null, RemoveCartItemBody>) => <
T extends LineItem | undefined = undefined
>(
ctx: { item?: T } = {}
) => {
const { item } = ctx
useHook: ({ fetch }: MutationHookContext<RemoveItemHook>) => () => {
const { mutate } = useCart()
const removeItem: RemoveItemFn<LineItem> = async (input) => {
const itemId = input?.id ?? item?.id
if (!itemId) {
throw new ValidationError({
message: 'Invalid input used for this operation',
})
}
return useCallback(
async function removeItem(input) {
const data = await fetch({ input: { itemId: input.id } })
await mutate(data, false)
const data = await fetch({ input: { itemId } })
await mutate(data, false)
return data
}
return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate])
return data
},
[fetch, mutate]
)
},
}

View File

@ -2,25 +2,30 @@ import { useCallback } from 'react'
import debounce from 'lodash.debounce'
import type {
HookFetcherContext,
MutationHook,
MutationHookContext,
} from '@commerce/utils/types'
import { ValidationError } from '@commerce/utils/errors'
import useUpdateItem, {
UpdateItemInput as UpdateItemInputBase,
UseUpdateItem,
} from '@commerce/cart/use-update-item'
// import useUpdateItem, {
// UpdateItemInput as UpdateItemInputBase,
// UseUpdateItem,
// } from '@commerce/cart/use-update-item'
import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item'
import useCart from './use-cart'
import { handler as removeItemHandler } from './use-remove-item'
import type { Cart, LineItem, UpdateCartItemBody } from '../types'
import { CartItemBody, LineItem } from '@commerce/types/cart'
import { checkoutToCart } from './utils'
export type UpdateItemInput<T = any> = T extends LineItem
? Partial<UpdateItemInputBase<LineItem>>
: UpdateItemInputBase<LineItem>
import { UpdateItemHook } from '../types/cart'
// export type UpdateItemInput<T = any> = T extends LineItem
// ? Partial<UpdateItemInputBase<LineItem>>
// : UpdateItemInputBase<LineItem>
export default useUpdateItem as UseUpdateItem<typeof handler>
export type UpdateItemActionInput<T = any> = T extends LineItem
? Partial<UpdateItemHook['actionInput']>
: UpdateItemHook['actionInput']
export const handler = {
fetchOptions: {
query: 'cart',
@ -30,7 +35,7 @@ export const handler = {
input: { itemId, item },
options,
fetch,
}: HookFetcherContext<UpdateCartItemBody>) {
}: HookFetcherContext<UpdateItemHook>) {
if (Number.isInteger(item.quantity)) {
// Also allow the update hook to remove an item if the quantity is lower than 1
if (item.quantity! < 1) {
@ -52,9 +57,7 @@ export const handler = {
return checkoutToCart(response)
},
useHook: ({
fetch,
}: MutationHookContext<Cart | null, UpdateCartItemBody>) => <
useHook: ({ fetch }: MutationHookContext<UpdateItemHook>) => <
T extends LineItem | undefined = undefined
>(
ctx: {
@ -66,7 +69,7 @@ export const handler = {
const { mutate, data: cartData } = useCart() as any
return useCallback(
debounce(async (input: UpdateItemInput<T>) => {
debounce(async (input: UpdateItemActionInput) => {
const firstLineItem = cartData.lineItems[0]
const itemId = item?.id || firstLineItem.id
const productId = item?.productId || firstLineItem.productId

View File

@ -9,7 +9,7 @@ export const checkoutCreate = async (fetch: any) => {
})
if (!cart) {
const cart = await fetch({
await fetch({
query: 'cart',
method: 'setItems',
variables: [[]],

View File

@ -1,33 +0,0 @@
import { HookFetcherFn } from '@commerce/utils/types'
import { Cart } from '@commerce/types'
// import { checkoutCreate, checkoutToCart } from '.'
import { FetchCartInput } from '@commerce/cart/use-cart'
import { data } from 'autoprefixer'
import { normalizeCart } from '../../utils'
const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({
options,
// input: { cartId: checkoutId },
fetch,
}) => {
let checkout
// if (checkoutId) {
const data = await fetch({
query: 'cart',
method: 'get',
// variables: { category: categoryId },
})
// checkout = data.node
// }
// if (checkout?.completedAt || !checkoutId) {
// checkout = await checkoutCreate(fetch)
// }
// TODO: Fix this type
// return checkoutToCart({ checkout } as any)
return normalizeCart(data)
}
export default fetcher

View File

@ -1,37 +0,0 @@
import { getConfig, SwellConfig } from '../api'
type Variables = {
first?: number
}
type ReturnType = {
pages: Page[]
}
export type Page = {
id: string
name: string
url: string
sort_order?: number
body: string
}
const getAllPages = async (options?: {
variables?: Variables
config: SwellConfig
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables = { first: 250 } } = options ?? {}
config = getConfig(config)
const { locale, fetch } = config
const data = await fetch('content', 'list', ['pages'])
const pages =
data?.results?.map(({ slug, ...rest }: { slug: string }) => ({
url: `/${locale}/${slug}`,
...rest,
})) ?? []
return { pages }
}
export default getAllPages

View File

@ -1,33 +0,0 @@
import { getConfig, SwellConfig } from '../api'
import { Page } from './get-all-pages'
type Variables = {
id: string
}
export type GetPageResult<T extends { page?: any } = { page?: Page }> = T
const getPage = async (options: {
variables: Variables
config: SwellConfig
preview?: boolean
}): Promise<GetPageResult> => {
let { config, variables } = options ?? {}
config = getConfig(config)
const { locale } = config
const { id } = variables
const result = await config.fetch('content', 'get', ['pages', id])
const page = result
return {
page: page
? {
...page,
url: `/${locale}/${page.slug}`,
}
: null,
}
}
export default getPage

View File

@ -1,31 +0,0 @@
import getCategories from '../utils/get-categories'
import getVendors, { Brands } from '../utils/get-vendors'
import { Category } from '@commerce/types'
import { getConfig, SwellConfig } from '../api'
export type GetSiteInfoResult<
T extends { categories: any[]; brands: any[] } = {
categories: Category[]
brands: Brands
}
> = T
const getSiteInfo = async (options?: {
variables?: any
config: SwellConfig
preview?: boolean
}): Promise<GetSiteInfoResult> => {
let { config } = options ?? {}
config = getConfig(config)
const categories = await getCategories(config)
const brands = await getVendors(config)
return {
categories,
brands,
}
}
export default getSiteInfo

View File

@ -1,11 +1,11 @@
import useCustomer, { UseCustomer } from '@commerce/customer/use-customer'
import { Customer } from '@commerce/types'
import { SWRHook } from '@commerce/utils/types'
import { normalizeCustomer } from '../utils/normalize'
import type { CustomerHook } from '../types/customer'
export default useCustomer as UseCustomer<typeof handler>
export const handler: SWRHook<Customer | null> = {
export const handler: SWRHook<CustomerHook> = {
fetchOptions: {
query: 'account',
method: 'get',
@ -25,26 +25,3 @@ export const handler: SWRHook<Customer | null> = {
})
},
}
// const handler = (): { data: Customer } => {
// const swell = getContext();
// const response = swell.account.get();
// const { firstName, lastName, email, company, customerGroupId, notes, phone,
// entityId, addressCount, attributeCount, storeCredit } = response;
// return {
// data: {
// firstName,
// lastName,
// email,
// company,
// customerGroupId,
// notes,
// phone,
// entityId,
// addressCount,
// attributeCount,
// storeCredit
// }
// }
// }
// export default handler;

View File

@ -1,28 +0,0 @@
import { CollectionEdge } from '../schema'
import { getConfig, SwellConfig } from '../api'
const getAllCollections = async (options?: {
variables?: any
config: SwellConfig
preview?: boolean
}) => {
let { config, variables = { limit: 25 } } = options ?? {}
config = getConfig(config)
const response = await config.fetch('categories', 'list', { variables })
const edges = response.results ?? []
const categories = edges.map(
({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({
entityId,
name,
path: `/${handle}`,
})
)
return {
categories,
}
}
export default getAllCollections

View File

@ -1,39 +0,0 @@
import { SwellProduct } from '../types'
import { getConfig, SwellConfig } from '../api'
type ProductPath = {
path: string
}
export type ProductPathNode = {
node: ProductPath
}
type ReturnType = {
products: ProductPathNode[]
}
const getAllProductPaths = async (options?: {
variables?: any
config?: SwellConfig
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables = [{ limit: 100 }] } = options ?? {}
config = getConfig(config)
const { results } = await config.fetch('products', 'list', [
{
limit: variables.first,
},
])
return {
products: results?.map(({ slug: handle }: SwellProduct) => ({
node: {
path: `/${handle}`,
},
})),
}
}
export default getAllProductPaths

View File

@ -1,36 +0,0 @@
import { getConfig, SwellConfig } from '../api'
import { normalizeProduct } from '../utils/normalize'
import { Product } from '@commerce/types'
import { SwellProduct } from '../types'
type Variables = {
first?: number
field?: string
}
type ReturnType = {
products: Product[]
}
const getAllProducts = async (options: {
variables?: Variables
config?: SwellConfig
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables = { first: 250 } } = options ?? {}
config = getConfig(config)
const { results } = await config.fetch('products', 'list', [
{
limit: variables.first,
},
])
const products = results.map((product: SwellProduct) =>
normalizeProduct(product)
)
return {
products,
}
}
export default getAllProducts

View File

@ -1,32 +0,0 @@
import { GraphQLFetcherResult } from '@commerce/api'
import { getConfig, SwellConfig } from '../api'
import { normalizeProduct } from '../utils'
type Variables = {
slug: string
}
type ReturnType = {
product: any
}
const getProduct = async (options: {
variables: Variables
config: SwellConfig
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables } = options ?? {}
config = getConfig(config)
const product = await config.fetch('products', 'get', [variables.slug])
if (product && product.variants) {
product.variants = product.variants?.results
}
return {
product: product ? normalizeProduct(product) : null,
}
}
export default getProduct

View File

@ -0,0 +1,2 @@
export { default as usePrice } from './use-price'
export { default as useSearch } from './use-search'

View File

@ -1,11 +1,8 @@
import { SWRHook } from '@commerce/utils/types'
import useSearch, { UseSearch } from '@commerce/product/use-search'
import { normalizeProduct } from '../utils'
import { Product } from '@commerce/types'
import { SwellProduct } from '../types'
import type { SearchProductsHook } from '../types/product'
export default useSearch as UseSearch<typeof handler>
@ -16,18 +13,9 @@ export type SearchProductsInput = {
sort?: string
}
export type SearchProductsData = {
products: Product[]
found: boolean
}
export const handler: SWRHook<
SearchProductsData,
SearchProductsInput,
SearchProductsInput
> = {
export const handler: SWRHook<SearchProductsHook> = {
fetchOptions: {
query: 'products', // String(Math.random()),
query: 'products',
method: 'list',
},
async fetcher({ input, options, fetch }) {

View File

@ -1,3 +1,5 @@
import { Provider } from '@commerce'
import { SWELL_CHECKOUT_URL_COOKIE, STORE_DOMAIN } from './const'
import { handler as useCart } from './cart/use-cart'
@ -14,18 +16,15 @@ import { handler as useSignup } from './auth/use-signup'
import fetcher from './fetcher'
export const swellProvider = {
export const swellProvider: Provider = {
locale: 'en-us',
cartCookie: SWELL_CHECKOUT_URL_COOKIE,
storeDomain: STORE_DOMAIN,
// storeDomain: STORE_DOMAIN,
fetcher,
cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
customer: { useCustomer },
products: { useSearch },
auth: { useLogin, useLogout, useSignup },
features: {
wishlist: false,
},
}
export type SwellProvider = typeof swellProvider

View File

@ -1,4 +1,5 @@
import * as Core from '@commerce/types'
import * as Core from '@commerce/types/cart'
import { Customer } from '@commerce/types'
import { CheckoutLineItem } from './schema'
export type SwellImage = {
@ -43,12 +44,18 @@ export type SwellVariant = {
name: string
price?: number
stock_status?: string
__type?: 'MultipleChoiceOption' | undefined
}
export interface SwellProductOptionValue {
id: string
label: string
hexColors?: string[]
}
export interface ProductOptionValue {
label: string
hexColors?: string[]
id: string
}
export type ProductOptions = {
@ -73,10 +80,7 @@ export interface SwellProduct {
variants: any[]
}
export interface SwellCustomer extends Core.Customer {
first_name: string
last_name: string
}
export type SwellCustomer = any
export type SwellCheckout = {
id: string
@ -106,17 +110,3 @@ export type CartItemBody = Core.CartItemBody & {
productId: string // The product id is always required for BC
optionSelections?: OptionSelections
}
export type GetCartHandlerBody = Core.GetCartHandlerBody
export type AddCartItemBody = Core.AddCartItemBody<CartItemBody>
export type AddCartItemHandlerBody = Core.AddCartItemHandlerBody<CartItemBody>
export type UpdateCartItemBody = Core.UpdateCartItemBody<CartItemBody>
export type UpdateCartItemHandlerBody = Core.UpdateCartItemHandlerBody<CartItemBody>
export type RemoveCartItemBody = Core.RemoveCartItemBody
export type RemoveCartItemHandlerBody = Core.RemoveCartItemHandlerBody

View File

@ -0,0 +1 @@
export * from '@commerce/types/cart'

View File

@ -0,0 +1 @@
export * from '@commerce/types/checkout'

View File

@ -0,0 +1 @@
export * from '@commerce/types/common'

View File

@ -0,0 +1 @@
export * from '@commerce/types/customer'

View File

@ -0,0 +1,25 @@
import * as Cart from './cart'
import * as Checkout from './checkout'
import * as Common from './common'
import * as Customer from './customer'
import * as Login from './login'
import * as Logout from './logout'
import * as Page from './page'
import * as Product from './product'
import * as Signup from './signup'
import * as Site from './site'
import * as Wishlist from './wishlist'
export type {
Cart,
Checkout,
Common,
Customer,
Login,
Logout,
Page,
Product,
Signup,
Site,
Wishlist,
}

View File

@ -0,0 +1,11 @@
import * as Core from '@commerce/types/login'
import { LoginBody, LoginTypes } from '@commerce/types/login'
export * from '@commerce/types/login'
export type LoginHook<T extends LoginTypes = LoginTypes> = {
data: null
actionInput: LoginBody
fetcherInput: LoginBody
body: T['body']
}

View File

@ -0,0 +1 @@
export * from '@commerce/types/logout'

View File

@ -0,0 +1 @@
export * from '@commerce/types/page'

View File

@ -0,0 +1 @@
export * from '@commerce/types/product'

View File

@ -0,0 +1 @@
export * from '@commerce/types/signup'

View File

@ -0,0 +1 @@
export * from '@commerce/types/site'

View File

@ -0,0 +1 @@
export * from '@commerce/types/wishlist'

View File

@ -1,5 +1,5 @@
import { SwellConfig } from '../api'
import { Category } from '@commerce/types'
import { Category } from '../types/site'
const getCategories = async (config: SwellConfig): Promise<Category[]> => {
const data = await config.fetch('categories', 'get')

View File

@ -1,6 +1,6 @@
import { Product, Customer } from '@commerce/types'
import { MoneyV2, ProductOption } from '../schema'
import { Customer } from '../types/customer'
import { Product, ProductOption } from '../types/product'
import { MoneyV2 } from '../schema'
import type {
Cart,
@ -10,6 +10,7 @@ import type {
SwellImage,
SwellVariant,
ProductOptionValue,
SwellProductOptionValue,
SwellCart,
LineItem,
} from '../types'
@ -21,8 +22,13 @@ const money = ({ amount, currencyCode }: MoneyV2) => {
}
}
type swellProductOption = {
id: string
name: string
values: any[]
}
type normalizedProductOption = {
__typename?: string
id: string
displayName: string
values: ProductOptionValue[]
@ -32,11 +38,11 @@ const normalizeProductOption = ({
id,
name: displayName = '',
values = [],
}: ProductOption) => {
}: swellProductOption): ProductOption => {
let returnValues = values.map((value) => {
let output: any = {
label: value.name,
id: value?.id || id,
// id: value?.id || id,
}
if (displayName.match(/colou?r/gi)) {
output = {
@ -68,7 +74,7 @@ const normalizeProductImages = (images: SwellImage[]) => {
const normalizeProductVariants = (
variants: SwellVariant[],
productOptions: normalizedProductOption[]
productOptions: swellProductOption[]
) => {
return variants?.map(
({ id, name, price, option_value_ids: optionValueIds = [] }) => {
@ -79,22 +85,22 @@ const normalizeProductVariants = (
const options = optionValueIds.map((id) => {
const matchingOption = productOptions.find((option) => {
return option.values.find(
(value: ProductOptionValue) => value.id == id
(value: SwellProductOptionValue) => value.id == id
)
})
return normalizeProductOption({
id,
name: matchingOption?.displayName ?? '',
name: matchingOption?.name ?? '',
values,
})
})
return {
id,
name,
// name,
// sku: sku ?? id,
price: price ?? null,
listPrice: price ?? null,
// price: price ?? null,
// listPrice: price ?? null,
// requiresShipping: true,
options,
}
@ -116,11 +122,12 @@ export function normalizeProduct(swellProduct: SwellProduct): Product {
} = swellProduct
// ProductView accesses variants for each product
const emptyVariants = [{ options: [], id, name }]
const productOptions = options
? options.map((o) => normalizeProductOption(o))
: []
const productVariants = variants
? normalizeProductVariants(variants, productOptions)
? normalizeProductVariants(variants, options)
: []
const productImages = normalizeProductImages(images)