mirror of
https://github.com/vercel/commerce.git
synced 2025-05-17 15:06:59 +00:00
Updated types, schemas & providers
This commit is contained in:
parent
163a22ec88
commit
ba0e7beb73
@ -1130,7 +1130,7 @@ export interface definitions {
|
||||
*/
|
||||
search_keywords?: string
|
||||
/**
|
||||
* Image URL used for this category on the storefront. Images can be uploaded via form file post to `/brands/{brandId}/image`, or by providing a publicly accessible URL in this field.
|
||||
* Image URL used for this category on the storefront. Images can be uploaded via form file post to `/{brandId}/image`, or by providing a publicly accessible URL in this field.
|
||||
*/
|
||||
image_url?: string
|
||||
custom_url?: definitions['customUrl_Full']
|
||||
|
@ -1,7 +1,6 @@
|
||||
import type { Wishlist } from '../../../types/wishlist'
|
||||
import type { WishlistEndpoint } from '.'
|
||||
import getCustomerId from '../../utils/get-customer-id'
|
||||
import getCustomerWishlist from '../../operations/get-customer-wishlist'
|
||||
|
||||
// Return wishlist info
|
||||
const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({
|
||||
|
@ -49,7 +49,7 @@ export default function getCustomerWishlistOperation({
|
||||
|
||||
if (includeProducts && wishlist?.items?.length) {
|
||||
const ids = wishlist.items
|
||||
?.map((item) => (item?.product_id ? String(item?.product_id) : null))
|
||||
?.map((item) => (item?.productId ? String(item?.productId) : null))
|
||||
.filter((id): id is string => !!id)
|
||||
|
||||
if (ids?.length) {
|
||||
@ -66,7 +66,7 @@ export default function getCustomerWishlistOperation({
|
||||
}, {})
|
||||
// Populate the wishlist items with the graphql products
|
||||
wishlist.items.forEach((item) => {
|
||||
const product = item && productsById[item.product_id!]
|
||||
const product = item && productsById[Number(item.productId)]
|
||||
if (item && product) {
|
||||
// @ts-ignore Fix this type when the wishlist type is properly defined
|
||||
item.product = product
|
||||
|
@ -7,7 +7,7 @@ import type { GetSiteInfoQuery } from '../../../schema'
|
||||
import filterEdges from '../utils/filter-edges'
|
||||
import type { BigcommerceConfig, Provider } from '..'
|
||||
import { categoryTreeItemFragment } from '../fragments/category-tree'
|
||||
import { normalizeCategory } from '../../lib/normalize'
|
||||
import { normalizeBrand, normalizeCategory } from '../../lib/normalize'
|
||||
|
||||
// Get 3 levels of categories
|
||||
export const getSiteInfoQuery = /* GraphQL */ `
|
||||
@ -79,7 +79,7 @@ export default function getSiteInfoOperation({
|
||||
|
||||
return {
|
||||
categories: categories ?? [],
|
||||
brands: filterEdges(brands),
|
||||
brands: filterEdges(brands).map(normalizeBrand),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { WishlistItemBody } from '../../types/wishlist'
|
||||
import type { CartItemBody, OptionSelections } from '../../types/cart'
|
||||
import type { CartItemBody, SelectedOption } from '../../types/cart'
|
||||
|
||||
type BCWishlistItemBody = {
|
||||
product_id: number
|
||||
@ -10,7 +10,7 @@ type BCCartItemBody = {
|
||||
product_id: number
|
||||
variant_id: number
|
||||
quantity?: number
|
||||
option_selections?: OptionSelections[]
|
||||
option_selections?: SelectedOption[]
|
||||
}
|
||||
|
||||
export const parseWishlistItem = (
|
||||
@ -24,5 +24,5 @@ export const parseCartItem = (item: CartItemBody): BCCartItemBody => ({
|
||||
quantity: item.quantity,
|
||||
product_id: Number(item.productId),
|
||||
variant_id: Number(item.variantId),
|
||||
option_selections: item.optionSelections,
|
||||
option_selections: item.optionsSelected,
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { Product } from '../types/product'
|
||||
import type { Cart, BigcommerceCart, LineItem } from '../types/cart'
|
||||
import type { Page } from '../types/page'
|
||||
import type { BCCategory, Category } from '../types/site'
|
||||
import type { BCCategory, BCBrand, Category, Brand } from '../types/site'
|
||||
import { definitions } from '../api/definitions/store-content'
|
||||
import update from './immutability'
|
||||
import getSlug from './get-slug'
|
||||
@ -54,7 +54,7 @@ export function normalizeProduct(productNode: any): Product {
|
||||
: [],
|
||||
},
|
||||
brand: {
|
||||
$apply: (brand: any) => (brand?.entityId ? brand?.entityId : null),
|
||||
$apply: (brand: any) => (brand?.id ? brand.id : null),
|
||||
},
|
||||
slug: {
|
||||
$set: path?.replace(/^\/+|\/+$/g, ''),
|
||||
@ -134,3 +134,12 @@ export function normalizeCategory(category: BCCategory): Category {
|
||||
path: category.path,
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeBrand(brand: BCBrand): Brand {
|
||||
return {
|
||||
id: `${brand.node.entityId}`,
|
||||
name: brand.node.name,
|
||||
slug: getSlug(brand.node.path),
|
||||
path: brand.node.path,
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import * as Core from '@vercel/commerce/types/cart'
|
||||
|
||||
export * from '@vercel/commerce/types/cart'
|
||||
|
||||
// TODO: this type should match:
|
||||
@ -24,43 +22,3 @@ export type BigcommerceCart = {
|
||||
discounts?: { id: number; discounted_amount: number }[]
|
||||
// TODO: add missing fields
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend core cart types
|
||||
*/
|
||||
|
||||
export type Cart = Core.Cart & {
|
||||
lineItems: Core.LineItem[]
|
||||
}
|
||||
|
||||
export type OptionSelections = {
|
||||
option_id: number
|
||||
option_value: number | string
|
||||
}
|
||||
|
||||
export type CartItemBody = Core.CartItemBody & {
|
||||
productId: string // The product id is always required for BC
|
||||
optionSelections?: OptionSelections[]
|
||||
}
|
||||
|
||||
export type CartTypes = {
|
||||
cart: Cart
|
||||
item: Core.LineItem
|
||||
itemBody: CartItemBody
|
||||
}
|
||||
|
||||
export type CartHooks = Core.CartHooks
|
||||
|
||||
export type GetCartHook = CartHooks['getCart']
|
||||
export type AddItemHook = CartHooks['addItem']
|
||||
export type UpdateItemHook = CartHooks['updateItem']
|
||||
export type RemoveItemHook = CartHooks['removeItem']
|
||||
|
||||
export type CartSchema = Core.CartSchema
|
||||
|
||||
export type CartHandlers = Core.CartHandlers
|
||||
|
||||
export type GetCartHandler = CartHandlers['getCart']
|
||||
export type AddItemHandler = CartHandlers['addItem']
|
||||
export type UpdateItemHandler = CartHandlers['updateItem']
|
||||
export type RemoveItemHandler = CartHandlers['removeItem']
|
||||
|
@ -1,5 +1 @@
|
||||
import * as Core from '@vercel/commerce/types/customer'
|
||||
|
||||
export * from '@vercel/commerce/types/customer'
|
||||
|
||||
export type CustomerSchema = Core.CustomerSchema
|
||||
|
@ -1,8 +1 @@
|
||||
import * as Core from '@vercel/commerce/types/login'
|
||||
import type { LoginMutationVariables } from '../../schema'
|
||||
|
||||
export * from '@vercel/commerce/types/login'
|
||||
|
||||
export type LoginOperation = Core.LoginOperation & {
|
||||
variables: LoginMutationVariables
|
||||
}
|
||||
|
@ -1,11 +1 @@
|
||||
import * as Core from '@vercel/commerce/types/page'
|
||||
export * from '@vercel/commerce/types/page'
|
||||
|
||||
export type Page = Core.Page
|
||||
|
||||
export type PageTypes = {
|
||||
page: Page
|
||||
}
|
||||
|
||||
export type GetAllPagesOperation = Core.GetAllPagesOperation<PageTypes>
|
||||
export type GetPageOperation = Core.GetPageOperation<PageTypes>
|
||||
|
@ -1,5 +1,4 @@
|
||||
import * as Core from '@vercel/commerce/types/site'
|
||||
import type { GetSiteInfoQuery, GetSiteInfoQueryVariables } from '../../schema'
|
||||
import type { GetSiteInfoQuery } from '../../schema'
|
||||
|
||||
export * from '@vercel/commerce/types/site'
|
||||
|
||||
@ -7,13 +6,6 @@ export type BCCategory = NonNullable<
|
||||
GetSiteInfoQuery['site']['categoryTree']
|
||||
>[0]
|
||||
|
||||
export type Brand = NonNullable<
|
||||
export type BCBrand = NonNullable<
|
||||
NonNullable<GetSiteInfoQuery['site']['brands']['edges']>[0]
|
||||
>
|
||||
|
||||
export type SiteTypes = {
|
||||
category: Core.Category
|
||||
brand: Brand
|
||||
}
|
||||
|
||||
export type GetSiteInfoOperation = Core.GetSiteInfoOperation<SiteTypes>
|
||||
|
@ -1,24 +1 @@
|
||||
import * as Core from '@vercel/commerce/types/wishlist'
|
||||
import { definitions } from '../api/definitions/wishlist'
|
||||
import type { ProductEdge } from '../api/operations/get-all-products'
|
||||
|
||||
export * from '@vercel/commerce/types/wishlist'
|
||||
|
||||
export type WishlistItem = NonNullable<
|
||||
definitions['wishlist_Full']['items']
|
||||
>[0] & {
|
||||
product?: ProductEdge['node']
|
||||
}
|
||||
|
||||
export type Wishlist = Omit<definitions['wishlist_Full'], 'items'> & {
|
||||
items?: WishlistItem[]
|
||||
}
|
||||
|
||||
export type WishlistTypes = {
|
||||
wishlist: Wishlist
|
||||
itemBody: Core.WishlistItemBody
|
||||
}
|
||||
|
||||
export type WishlistSchema = Core.WishlistSchema<WishlistTypes>
|
||||
export type GetCustomerWishlistOperation =
|
||||
Core.GetCustomerWishlistOperation<WishlistTypes>
|
||||
|
@ -3,8 +3,10 @@ import { CommerceAPIError } from '../utils/errors'
|
||||
import isAllowedOperation from '../utils/is-allowed-operation'
|
||||
import type { GetAPISchema } from '..'
|
||||
|
||||
const cartEndpoint: GetAPISchema<any, CartSchema<any>>['endpoint']['handler'] =
|
||||
async (ctx) => {
|
||||
const cartEndpoint: GetAPISchema<
|
||||
any,
|
||||
CartSchema
|
||||
>['endpoint']['handler'] = async (ctx) => {
|
||||
const { req, res, handlers, config } = ctx
|
||||
|
||||
if (
|
||||
@ -55,6 +57,6 @@ const cartEndpoint: GetAPISchema<any, CartSchema<any>>['endpoint']['handler'] =
|
||||
|
||||
res.status(500).json({ data: null, errors: [{ message }] })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default cartEndpoint
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { ProductsSchema } from '../../../types/product'
|
||||
import { CommerceAPIError } from '../../utils/errors'
|
||||
import { getErrorMessage } from '../../utils/errors'
|
||||
import isAllowedOperation from '../../utils/is-allowed-operation'
|
||||
import type { GetAPISchema } from '../..'
|
||||
|
||||
@ -18,12 +18,7 @@ const productsEndpoint: GetAPISchema<
|
||||
return await handlers['getProducts']({ ...ctx, body })
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
const message =
|
||||
error instanceof CommerceAPIError
|
||||
? 'An unexpected error ocurred with the Commerce API'
|
||||
: 'An unexpected error ocurred'
|
||||
|
||||
const message = getErrorMessage(error)
|
||||
res.status(500).json({ data: null, errors: [{ message }] })
|
||||
}
|
||||
}
|
||||
|
0
packages/commerce/src/api/endpoints/index.ts
Normal file
0
packages/commerce/src/api/endpoints/index.ts
Normal file
@ -1,11 +1,11 @@
|
||||
import type { LoginSchema } from '../../types/login'
|
||||
import { CommerceAPIError } from '../utils/errors'
|
||||
import { CommerceAPIError, getErrorMessage } from '../utils/errors'
|
||||
import isAllowedOperation from '../utils/is-allowed-operation'
|
||||
import type { GetAPISchema } from '..'
|
||||
|
||||
const loginEndpoint: GetAPISchema<
|
||||
any,
|
||||
LoginSchema<any>
|
||||
LoginSchema
|
||||
>['endpoint']['handler'] = async (ctx) => {
|
||||
const { req, res, handlers } = ctx
|
||||
|
||||
@ -24,10 +24,7 @@ const loginEndpoint: GetAPISchema<
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
const message =
|
||||
error instanceof CommerceAPIError
|
||||
? 'An unexpected error ocurred with the Commerce API'
|
||||
: 'An unexpected error ocurred'
|
||||
const message = getErrorMessage(error)
|
||||
|
||||
res.status(500).json({ data: null, errors: [{ message }] })
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import type { GetAPISchema } from '..'
|
||||
|
||||
const wishlistEndpoint: GetAPISchema<
|
||||
any,
|
||||
WishlistSchema<any>
|
||||
WishlistSchema
|
||||
>['endpoint']['handler'] = async (ctx) => {
|
||||
const { req, res, handlers, config } = ctx
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { ZodError } from 'zod'
|
||||
|
||||
import type { Response } from '@vercel/fetch'
|
||||
import { CommerceError } from '../../utils/errors'
|
||||
|
||||
export class CommerceAPIError extends Error {
|
||||
status: number
|
||||
@ -20,3 +23,38 @@ export class CommerceNetworkError extends Error {
|
||||
this.name = 'CommerceNetworkError'
|
||||
}
|
||||
}
|
||||
|
||||
export const getErrorMessage = (error: unknown) => {
|
||||
return error instanceof CommerceAPIError
|
||||
? 'An unexpected error ocurred with the Commerce API'
|
||||
: 'An unexpected error ocurred'
|
||||
}
|
||||
|
||||
export const getOperationError = (operation: string, error: unknown) => {
|
||||
if (error instanceof ZodError) {
|
||||
error = new CommerceError({
|
||||
code: 'SCHEMA_VALIDATION_ERROR',
|
||||
message:
|
||||
`The ${operation} operation returned invalid data and has ${
|
||||
error.issues.length
|
||||
} parse ${error.issues.length === 1 ? 'error' : 'errors'}: \n` +
|
||||
error.issues
|
||||
.map(
|
||||
(e, index) =>
|
||||
`Error #${index + 1} ${
|
||||
e.path.length > 0 ? `Path: ${e.path.join('.')}, ` : ''
|
||||
}Code: ${e.code}, Message: ${e.message}`
|
||||
)
|
||||
.join('\n'),
|
||||
})
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
return new CommerceError({
|
||||
code: 'OPERATION_ERROR',
|
||||
message: `An unexpected error ocurred with the ${operation} operation: ${error.message}`,
|
||||
})
|
||||
}
|
||||
|
||||
return error
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
import { productSchema } from '../../schemas/product'
|
||||
import { CommerceError } from '../../utils/errors'
|
||||
|
||||
import type { AllowedOperations, OperationsData } from '../operations'
|
||||
|
||||
import { z } from 'zod'
|
||||
import { getOperationError } from './errors'
|
||||
import { pageSchema } from '../../schemas/page'
|
||||
import { siteInfoSchema } from '../../schemas/site'
|
||||
import { productSchema, productsPathsSchema } from '../../schemas/product'
|
||||
|
||||
export const withSchemaParser =
|
||||
(
|
||||
operation: AllowedOperations,
|
||||
@ -16,27 +17,7 @@ export const withSchemaParser =
|
||||
parse(operation, result)
|
||||
return result
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return Promise.reject(
|
||||
new CommerceError({
|
||||
code: 'SCHEMA_VALIDATION_ERROR',
|
||||
message:
|
||||
`The ${operation} opration returned invalid data and has ${
|
||||
error.issues.length
|
||||
} parse ${error.issues.length === 1 ? 'error' : 'errors'}: \n` +
|
||||
error.issues
|
||||
.map(
|
||||
(e, index) =>
|
||||
`${index + 1}. Property ${e.path.join('.')} (${e.code}): ${
|
||||
e.message
|
||||
}`
|
||||
)
|
||||
.join('\n'),
|
||||
})
|
||||
)
|
||||
} else {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
return Promise.reject(getOperationError(operation, error))
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,11 +28,26 @@ const parse = (operation: AllowedOperations, data: OperationsData) => {
|
||||
break
|
||||
|
||||
case 'getAllProducts':
|
||||
data.products?.forEach((product: any) => productSchema.parse(product))
|
||||
z.array(productSchema).parse(data.products)
|
||||
break
|
||||
|
||||
case 'getAllProductPaths':
|
||||
data.products?.forEach((p) => z.string().parse(p.path))
|
||||
productsPathsSchema.parse(data.products)
|
||||
break
|
||||
|
||||
case 'getPage':
|
||||
pageSchema.parse(data.page)
|
||||
break
|
||||
|
||||
case 'getAllPages':
|
||||
z.array(pageSchema).parse(data.pages)
|
||||
break
|
||||
|
||||
case 'getSiteInfo':
|
||||
siteInfoSchema.parse({
|
||||
categories: data.categories,
|
||||
brands: data.brands,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import type { LoginHook } from '../types/login'
|
||||
import type { Provider } from '..'
|
||||
|
||||
export type UseLogin<
|
||||
H extends MutationHook<LoginHook<any>> = MutationHook<LoginHook>
|
||||
H extends MutationHook<LoginHook> = MutationHook<LoginHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<LoginHook> = mutationFetcher
|
||||
|
@ -5,7 +5,7 @@ import type { LogoutHook } from '../types/logout'
|
||||
import type { Provider } from '..'
|
||||
|
||||
export type UseLogout<
|
||||
H extends MutationHook<LogoutHook<any>> = MutationHook<LogoutHook>
|
||||
H extends MutationHook<LogoutHook> = MutationHook<LogoutHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<LogoutHook> = mutationFetcher
|
||||
|
@ -5,7 +5,7 @@ import type { SignupHook } from '../types/signup'
|
||||
import type { Provider } from '..'
|
||||
|
||||
export type UseSignup<
|
||||
H extends MutationHook<SignupHook<any>> = MutationHook<SignupHook>
|
||||
H extends MutationHook<SignupHook> = MutationHook<SignupHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<SignupHook> = mutationFetcher
|
||||
|
@ -7,7 +7,7 @@ import { useHook, useSWRHook } from '../utils/use-hook'
|
||||
import { Provider, useCommerce } from '..'
|
||||
|
||||
export type UseCheckout<
|
||||
H extends SWRHook<GetCheckoutHook<any>> = SWRHook<GetCheckoutHook>
|
||||
H extends SWRHook<GetCheckoutHook> = SWRHook<GetCheckoutHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<GetCheckoutHook> = async ({
|
||||
|
@ -6,9 +6,7 @@ import { useHook, useMutationHook } from '../utils/use-hook'
|
||||
import { mutationFetcher } from '../utils/default-fetcher'
|
||||
|
||||
export type UseSubmitCheckout<
|
||||
H extends MutationHook<
|
||||
SubmitCheckoutHook<any>
|
||||
> = MutationHook<SubmitCheckoutHook>
|
||||
H extends MutationHook<SubmitCheckoutHook> = MutationHook<SubmitCheckoutHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<SubmitCheckoutHook> = mutationFetcher
|
||||
|
@ -1,5 +0,0 @@
|
||||
import * as product from './product'
|
||||
|
||||
export default {
|
||||
product,
|
||||
}
|
18
packages/commerce/src/schemas/page.ts
Normal file
18
packages/commerce/src/schemas/page.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
export const pageSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
url: z.string().startsWith('/'),
|
||||
body: z.string(),
|
||||
is_visible: z.boolean().optional(),
|
||||
sort_order: z.number().optional(),
|
||||
})
|
||||
|
||||
export const pagesPathsSchema = z.array(
|
||||
z.object({
|
||||
page: z.object({
|
||||
path: z.string(),
|
||||
}),
|
||||
})
|
||||
)
|
@ -2,7 +2,7 @@ import { z } from 'zod'
|
||||
|
||||
export const productPriceSchema = z.object({
|
||||
value: z.number(),
|
||||
currencyCode: z.string().optional(),
|
||||
currencyCode: z.string().max(3).optional(),
|
||||
retailPrice: z.number().optional(),
|
||||
})
|
||||
|
||||
@ -18,7 +18,7 @@ export const productOptionSchema = z.object({
|
||||
})
|
||||
|
||||
export const productImageSchema = z.object({
|
||||
url: z.string(),
|
||||
url: z.string().url().or(z.string().startsWith('/')),
|
||||
alt: z.string().optional(),
|
||||
width: z.number().optional(),
|
||||
height: z.number().optional(),
|
||||
@ -26,7 +26,7 @@ export const productImageSchema = z.object({
|
||||
|
||||
export const productVariantSchema = z.object({
|
||||
id: z.string(),
|
||||
sku: z.string(),
|
||||
sku: z.string().optional(),
|
||||
name: z.string(),
|
||||
options: z.array(productOptionSchema),
|
||||
image: productImageSchema.optional(),
|
||||
@ -46,3 +46,15 @@ export const productSchema = z.object({
|
||||
options: z.array(productOptionSchema),
|
||||
vendor: z.string().optional(),
|
||||
})
|
||||
|
||||
export const productsPathsSchema = z.array(
|
||||
z.object({ path: z.string().startsWith('/') })
|
||||
)
|
||||
|
||||
export const searchProductBodySchema = z.object({
|
||||
search: z.string(),
|
||||
categoryId: z.string(),
|
||||
brandId: z.string().optional(),
|
||||
sort: z.string().optional(),
|
||||
locale: z.string().optional(),
|
||||
})
|
||||
|
18
packages/commerce/src/schemas/site.ts
Normal file
18
packages/commerce/src/schemas/site.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
export const siteInfoSchema = z.object({
|
||||
categories: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
path: z.string().startsWith('/'),
|
||||
})
|
||||
),
|
||||
brands: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
path: z.string().startsWith('/'),
|
||||
})
|
||||
),
|
||||
})
|
@ -3,13 +3,13 @@ import type { Discount, Image } from './common'
|
||||
// TODO: This should use the same type as the `ProductVariant` type from `product.ts`
|
||||
export interface ProductVariant {
|
||||
/**
|
||||
* The variant's id.
|
||||
* The unique identifier for the variant.
|
||||
*/
|
||||
id: string | number
|
||||
id: string
|
||||
/**
|
||||
* The SKU (stock keeping unit) associated with the product variant.
|
||||
*/
|
||||
sku: string
|
||||
sku?: string
|
||||
/**
|
||||
* The product variant’s name, or the product's name.
|
||||
*/
|
||||
@ -19,7 +19,7 @@ export interface ProductVariant {
|
||||
*/
|
||||
price: number
|
||||
/**
|
||||
* Product variant’s price, as quoted by the manufacturer/distributor.
|
||||
* The product variant’s price before discounts are applied.
|
||||
*/
|
||||
listPrice: number
|
||||
/**
|
||||
@ -39,38 +39,38 @@ export interface ProductVariant {
|
||||
|
||||
export interface SelectedOption {
|
||||
/**
|
||||
* The selected option's id
|
||||
* The unique identifier for the option.
|
||||
*/
|
||||
id?: string
|
||||
/**
|
||||
* The product option’s name.
|
||||
* The product option’s name, such as "Color" or "Size".
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* The product option’s value.
|
||||
* The product option’s value, such as "Red" or "XL".
|
||||
*/
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface LineItem {
|
||||
/**
|
||||
* The line item's id.
|
||||
* The unique identifier for the line item.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* The product variant’s id.
|
||||
* The unique identifier for the product variant.
|
||||
*/
|
||||
variantId: string
|
||||
/**
|
||||
* The product's id.
|
||||
* The unique identifier for the product, if the variant is not provided.
|
||||
*/
|
||||
productId: string
|
||||
/**
|
||||
* The name of the line item.
|
||||
* This is usually the product's name.
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* List of discounts applied to the line item.
|
||||
* The quantity of the product variant in the line item.
|
||||
*/
|
||||
quantity: number
|
||||
/**
|
||||
@ -86,7 +86,7 @@ export interface LineItem {
|
||||
*/
|
||||
variant: ProductVariant
|
||||
/**
|
||||
* List of selected options.
|
||||
* List of selected options, to be used when displaying the line item, such as Color: Red, Size: XL.
|
||||
*/
|
||||
options?: SelectedOption[]
|
||||
}
|
||||
@ -96,7 +96,7 @@ export interface LineItem {
|
||||
*/
|
||||
export interface Cart {
|
||||
/**
|
||||
* The cart's id.
|
||||
* The unique identifier for the cart.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
@ -119,7 +119,7 @@ export interface Cart {
|
||||
* The currency used for this cart */
|
||||
currency: { code: string }
|
||||
/**
|
||||
* Specifies if taxes are included in the line items.
|
||||
* Indicates if taxes are included in the line items.
|
||||
*/
|
||||
taxesIncluded: boolean
|
||||
/**
|
||||
@ -135,12 +135,13 @@ export interface Cart {
|
||||
* Price of the cart before duties, shipping and taxes.*/
|
||||
subtotalPrice: number
|
||||
/**
|
||||
* The sum of all the prices of all the items in the cart.*/
|
||||
/**
|
||||
* Duties, taxes and discounts included.*/
|
||||
* The sum of all the prices of all the items in the cart.
|
||||
* Duties, taxes and discounts included.
|
||||
*/
|
||||
totalPrice: number
|
||||
/**
|
||||
* Discounts that have been applied on the cart.*/
|
||||
* Discounts that have been applied on the cart.
|
||||
*/
|
||||
discounts?: Discount[]
|
||||
}
|
||||
|
||||
@ -149,17 +150,22 @@ export interface Cart {
|
||||
*/
|
||||
export interface CartItemBody {
|
||||
/**
|
||||
* The product variant's id.
|
||||
* The unique identifier for the product variant.
|
||||
*/
|
||||
variantId: string
|
||||
/**
|
||||
* The product's id.
|
||||
* The unique identifier for the product, if the variant is not provided.
|
||||
*/
|
||||
productId?: string
|
||||
/**
|
||||
* The quantity of the product variant.
|
||||
*/
|
||||
quantity?: number
|
||||
|
||||
/**
|
||||
* The product variant's selected options.
|
||||
*/
|
||||
optionsSelected?: SelectedOption[]
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,6 +5,8 @@ import type { Card, CardFields } from './customer/card'
|
||||
// Index
|
||||
export type Checkout = any
|
||||
|
||||
export type CheckoutBody = any
|
||||
|
||||
export type CheckoutTypes = {
|
||||
card?: Card | CardFields
|
||||
address?: Address | AddressFields
|
||||
@ -13,45 +15,43 @@ export type CheckoutTypes = {
|
||||
hasShipping?: boolean
|
||||
}
|
||||
|
||||
export type SubmitCheckoutHook<T extends CheckoutTypes = CheckoutTypes> = {
|
||||
data: T
|
||||
input?: T
|
||||
fetcherInput: T
|
||||
body: { item: T }
|
||||
actionInput: T
|
||||
export type SubmitCheckoutHook = {
|
||||
data: Checkout
|
||||
input?: CheckoutBody
|
||||
fetcherInput: CheckoutBody
|
||||
body: { item: CheckoutBody }
|
||||
actionInput: CheckoutBody
|
||||
}
|
||||
|
||||
export type GetCheckoutHook<T extends CheckoutTypes = CheckoutTypes> = {
|
||||
data: T['checkout'] | null
|
||||
export type GetCheckoutHook = {
|
||||
data: Checkout | null | undefined
|
||||
input: {}
|
||||
fetcherInput: { cartId?: string }
|
||||
swrState: { isEmpty: boolean }
|
||||
mutations: { submit: UseSubmitCheckout }
|
||||
}
|
||||
|
||||
export type CheckoutHooks<T extends CheckoutTypes = CheckoutTypes> = {
|
||||
submitCheckout?: SubmitCheckoutHook<T>
|
||||
getCheckout: GetCheckoutHook<T>
|
||||
export type CheckoutHooks = {
|
||||
submitCheckout?: SubmitCheckoutHook
|
||||
getCheckout: GetCheckoutHook
|
||||
}
|
||||
|
||||
export type GetCheckoutHandler<T extends CheckoutTypes = CheckoutTypes> =
|
||||
GetCheckoutHook<T> & {
|
||||
export type GetCheckoutHandler = GetCheckoutHook & {
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type SubmitCheckoutHandler<T extends CheckoutTypes = CheckoutTypes> =
|
||||
SubmitCheckoutHook<T> & {
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type CheckoutHandlers<T extends CheckoutTypes = CheckoutTypes> = {
|
||||
getCheckout: GetCheckoutHandler<T>
|
||||
submitCheckout?: SubmitCheckoutHandler<T>
|
||||
}
|
||||
|
||||
export type CheckoutSchema<T extends CheckoutTypes = CheckoutTypes> = {
|
||||
export type SubmitCheckoutHandler = SubmitCheckoutHook & {
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type CheckoutHandlers = {
|
||||
getCheckout: GetCheckoutHandler
|
||||
submitCheckout?: SubmitCheckoutHandler
|
||||
}
|
||||
|
||||
export type CheckoutSchema = {
|
||||
endpoint: {
|
||||
options: {}
|
||||
handlers: CheckoutHandlers<T>
|
||||
handlers: CheckoutHandlers
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ export interface Measurement {
|
||||
*/
|
||||
value: number
|
||||
/**
|
||||
* The measurement's unit.
|
||||
* The measurement's unit, such as "KILOGRAMS", "GRAMS", "POUNDS" & "OOUNCES".
|
||||
*/
|
||||
unit: 'KILOGRAMS' | 'GRAMS' | 'POUNDS' | 'OUNCES'
|
||||
}
|
||||
|
@ -1,28 +1,62 @@
|
||||
export interface Address {
|
||||
/**
|
||||
* The unique identifier for the address.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* The customer's first name.
|
||||
*/
|
||||
mask: string
|
||||
}
|
||||
|
||||
export interface AddressFields {
|
||||
/**
|
||||
* The type of address.
|
||||
* @example "billing, shipping"
|
||||
*/
|
||||
type: string
|
||||
/**
|
||||
* The customer's first name.
|
||||
*/
|
||||
firstName: string
|
||||
/**
|
||||
* The customer's last name.
|
||||
*/
|
||||
lastName: string
|
||||
/**
|
||||
* Company name.
|
||||
*/
|
||||
company: string
|
||||
/**
|
||||
* The customer's billing address street number.
|
||||
*/
|
||||
streetNumber: string
|
||||
/**
|
||||
* The customer's billing address apartment number.
|
||||
*/
|
||||
apartments: string
|
||||
/**
|
||||
* The customer's billing address zip code.
|
||||
*/
|
||||
zipCode: string
|
||||
/**
|
||||
* The customer's billing address city.
|
||||
*/
|
||||
city: string
|
||||
/**
|
||||
* The customer's billing address country.
|
||||
*/
|
||||
country: string
|
||||
}
|
||||
|
||||
export type GetAddressesHook = {
|
||||
export interface GetAddressesHook {
|
||||
data: Address[] | null
|
||||
input: {}
|
||||
fetcherInput: { cartId?: string }
|
||||
swrState: { isEmpty: boolean }
|
||||
}
|
||||
|
||||
export type AddItemHook = {
|
||||
export interface AddItemHook {
|
||||
data: Address
|
||||
input?: AddressFields
|
||||
fetcherInput: AddressFields
|
||||
@ -30,7 +64,7 @@ export type AddItemHook = {
|
||||
actionInput: AddressFields
|
||||
}
|
||||
|
||||
export type UpdateItemHook = {
|
||||
export interface UpdateItemHook {
|
||||
data: Address | null
|
||||
input: { item?: AddressFields; wait?: number }
|
||||
fetcherInput: { itemId: string; item: AddressFields }
|
||||
@ -38,25 +72,21 @@ export type UpdateItemHook = {
|
||||
actionInput: AddressFields & { id: string }
|
||||
}
|
||||
|
||||
export type RemoveItemHook = {
|
||||
data: Address | null
|
||||
export interface RemoveItemHook {
|
||||
data: Address | null | undefined
|
||||
input: { item?: Address }
|
||||
fetcherInput: { itemId: string }
|
||||
body: { itemId: string }
|
||||
actionInput: { id: string }
|
||||
}
|
||||
|
||||
export type CustomerAddressHooks = {
|
||||
export interface CustomerAddressHooks {
|
||||
getAddresses: GetAddressesHook
|
||||
addItem: AddItemHook
|
||||
updateItem: UpdateItemHook
|
||||
removeItem: RemoveItemHook
|
||||
}
|
||||
|
||||
export type AddressHandler = GetAddressesHook & {
|
||||
body: { cartId?: string }
|
||||
}
|
||||
|
||||
export type AddItemHandler = AddItemHook & {
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
@ -1,102 +1,156 @@
|
||||
export interface Card {
|
||||
/**
|
||||
* Unique identifier for the card.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* Masked card number.
|
||||
* @example "************4242"
|
||||
*/
|
||||
mask: string
|
||||
/**
|
||||
* The card's brand.
|
||||
* @example "Visa, Mastercard, etc."
|
||||
*/
|
||||
provider: string
|
||||
}
|
||||
|
||||
/**
|
||||
* The fields required to create a new card.
|
||||
*/
|
||||
export interface CardFields {
|
||||
/**
|
||||
* Name on the card.
|
||||
*/
|
||||
cardHolder: string
|
||||
/**
|
||||
* The card's number, consisting of 16 digits.
|
||||
*/
|
||||
cardNumber: string
|
||||
/**
|
||||
* The card's expiry month and year, in the format MM/YY.
|
||||
* @example "01/25"
|
||||
*/
|
||||
cardExpireDate: string
|
||||
/**
|
||||
* The card's security code, consisting of 3 digits.
|
||||
*/
|
||||
cardCvc: string
|
||||
/**
|
||||
* The customer's first name.
|
||||
*/
|
||||
firstName: string
|
||||
/**
|
||||
* The customer's last name.
|
||||
*/
|
||||
lastName: string
|
||||
/**
|
||||
* Company name.
|
||||
*/
|
||||
company: string
|
||||
/**
|
||||
* The customer's billing address street number.
|
||||
*/
|
||||
streetNumber: string
|
||||
/**
|
||||
* The customer's billing address zip code.
|
||||
*/
|
||||
zipCode: string
|
||||
/**
|
||||
* The customer's billing address city.
|
||||
*/
|
||||
city: string
|
||||
/**
|
||||
* The customer's billing address country.
|
||||
*/
|
||||
country: string
|
||||
}
|
||||
|
||||
export type CustomerCardTypes = {
|
||||
card?: Card
|
||||
fields: CardFields
|
||||
}
|
||||
|
||||
export type GetCardsHook<T extends CustomerCardTypes = CustomerCardTypes> = {
|
||||
data: T['card'][] | null
|
||||
/**
|
||||
* Hook for getting a customer's cards.
|
||||
*/
|
||||
export interface GetCardsHook {
|
||||
data: Card[] | null
|
||||
input: {}
|
||||
fetcherInput: { cartId?: string }
|
||||
swrState: { isEmpty: boolean }
|
||||
}
|
||||
|
||||
export type AddItemHook<T extends CustomerCardTypes = CustomerCardTypes> = {
|
||||
data: T['card']
|
||||
input?: T['fields']
|
||||
fetcherInput: T['fields']
|
||||
body: { item: T['fields'] }
|
||||
actionInput: T['fields']
|
||||
/**
|
||||
* Hook for adding a card to a customer's account.
|
||||
*/
|
||||
export interface AddItemHook {
|
||||
data: Card
|
||||
input?: CardFields
|
||||
fetcherInput: CardFields
|
||||
body: { item: CardFields }
|
||||
actionInput: CardFields
|
||||
}
|
||||
|
||||
export type UpdateItemHook<T extends CustomerCardTypes = CustomerCardTypes> = {
|
||||
data: T['card'] | null
|
||||
input: { item?: T['fields']; wait?: number }
|
||||
fetcherInput: { itemId: string; item: T['fields'] }
|
||||
body: { itemId: string; item: T['fields'] }
|
||||
actionInput: T['fields'] & { id: string }
|
||||
/**
|
||||
* Hook for updating a card from a customer's account.
|
||||
*/
|
||||
export interface UpdateItemHook {
|
||||
data: Card | null | undefined
|
||||
input: { item?: CardFields; wait?: number }
|
||||
fetcherInput: { itemId: string; item: CardFields }
|
||||
body: { itemId: string; item: CardFields }
|
||||
actionInput: CardFields & { id: string }
|
||||
}
|
||||
|
||||
export type RemoveItemHook<T extends CustomerCardTypes = CustomerCardTypes> = {
|
||||
data: T['card'] | null
|
||||
input: { item?: T['card'] }
|
||||
/**
|
||||
* Hook for removing a card from a customer's account.
|
||||
*/
|
||||
export interface RemoveItemHook {
|
||||
data: Card | null | undefined
|
||||
input: { item?: Card }
|
||||
fetcherInput: { itemId: string }
|
||||
body: { itemId: string }
|
||||
actionInput: { id: string }
|
||||
}
|
||||
|
||||
export type CustomerCardHooks<T extends CustomerCardTypes = CustomerCardTypes> =
|
||||
{
|
||||
getCards: GetCardsHook<T>
|
||||
addItem: AddItemHook<T>
|
||||
updateItem: UpdateItemHook<T>
|
||||
removeItem: RemoveItemHook<T>
|
||||
}
|
||||
|
||||
export type CardsHandler<T extends CustomerCardTypes = CustomerCardTypes> =
|
||||
GetCardsHook<T> & {
|
||||
body: { cartId?: string }
|
||||
}
|
||||
|
||||
export type AddItemHandler<T extends CustomerCardTypes = CustomerCardTypes> =
|
||||
AddItemHook<T> & {
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type UpdateItemHandler<T extends CustomerCardTypes = CustomerCardTypes> =
|
||||
UpdateItemHook<T> & {
|
||||
data: T['card']
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type RemoveItemHandler<T extends CustomerCardTypes = CustomerCardTypes> =
|
||||
RemoveItemHook<T> & {
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type CustomerCardHandlers<
|
||||
T extends CustomerCardTypes = CustomerCardTypes
|
||||
> = {
|
||||
getCards: GetCardsHook<T>
|
||||
addItem: AddItemHandler<T>
|
||||
updateItem: UpdateItemHandler<T>
|
||||
removeItem: RemoveItemHandler<T>
|
||||
/**
|
||||
* Hooks for add, update & remove items from the cart.
|
||||
*/
|
||||
export interface CustomerCardHooks {
|
||||
getCards: GetCardsHook
|
||||
addItem: AddItemHook
|
||||
updateItem: UpdateItemHook
|
||||
removeItem: RemoveItemHook
|
||||
}
|
||||
|
||||
export type CustomerCardSchema<
|
||||
T extends CustomerCardTypes = CustomerCardTypes
|
||||
> = {
|
||||
/**
|
||||
* Customer card API handler
|
||||
*/
|
||||
export type AddItemHandler = AddItemHook & {
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type UpdateItemHandler = UpdateItemHook & {
|
||||
data: Card
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type RemoveItemHandler = RemoveItemHook & {
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
/**
|
||||
* Customer card API handlers.
|
||||
*/
|
||||
export type CustomerCardHandlers = {
|
||||
getCards: GetCardsHook
|
||||
addItem: AddItemHandler
|
||||
updateItem: UpdateItemHandler
|
||||
removeItem: RemoveItemHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Customer card API endpoints.
|
||||
*/
|
||||
export type CustomerCardSchema = {
|
||||
endpoint: {
|
||||
options: {}
|
||||
handlers: CustomerCardHandlers<T>
|
||||
handlers: CustomerCardHandlers
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,26 @@
|
||||
export type LoginBody = {
|
||||
export interface LoginBody {
|
||||
/**
|
||||
* The user's email address.
|
||||
*/
|
||||
email: string
|
||||
/**
|
||||
* The user's password.
|
||||
*/
|
||||
password: string
|
||||
}
|
||||
|
||||
export type LoginTypes = {
|
||||
body: LoginBody
|
||||
}
|
||||
|
||||
export type LoginHook<T extends LoginTypes = LoginTypes> = {
|
||||
export interface LoginHook {
|
||||
data: null
|
||||
actionInput: LoginBody
|
||||
fetcherInput: LoginBody
|
||||
body: T['body']
|
||||
body: LoginBody
|
||||
}
|
||||
|
||||
export type LoginSchema<T extends LoginTypes = LoginTypes> = {
|
||||
export type LoginSchema = {
|
||||
endpoint: {
|
||||
options: {}
|
||||
handlers: {
|
||||
login: LoginHook<T>
|
||||
login: LoginHook
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,15 @@
|
||||
export type LogoutTypes = {
|
||||
body: { redirectTo?: string }
|
||||
}
|
||||
|
||||
export type LogoutHook<T extends LogoutTypes = LogoutTypes> = {
|
||||
export interface LogoutHook {
|
||||
data: null
|
||||
body: T['body']
|
||||
body: {
|
||||
redirectTo?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type LogoutSchema<T extends LogoutTypes = LogoutTypes> = {
|
||||
export type LogoutSchema = {
|
||||
endpoint: {
|
||||
options: {}
|
||||
handlers: {
|
||||
logout: LogoutHook<T>
|
||||
logout: LogoutHook
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,43 @@
|
||||
// TODO: define this type
|
||||
export type Page = {
|
||||
// ID of the Web page.
|
||||
/**
|
||||
* The unique identifier for the page.
|
||||
*/
|
||||
id: string
|
||||
// Page name, as displayed on the storefront.
|
||||
/**
|
||||
* Page name, as displayed on the storefront.
|
||||
*/
|
||||
name: string
|
||||
// Relative URL on the storefront for this page.
|
||||
/**
|
||||
* Relative URL on the storefront for this page.
|
||||
*/
|
||||
url?: string
|
||||
// HTML or variable that populates this page’s `<body>` element, in default/desktop view. Required in POST if page type is `raw`.
|
||||
/**
|
||||
* HTML or variable that populates this page’s `<body>` element, in default/desktop view. Required in POST if page type is `raw`.
|
||||
*/
|
||||
body: string
|
||||
// If true, this page appears in the storefront’s navigation menu.
|
||||
/**
|
||||
* If true, this page appears in the storefront’s navigation menu.
|
||||
*/
|
||||
is_visible?: boolean
|
||||
// Order in which this page should display on the storefront. (Lower integers specify earlier display.)
|
||||
/**
|
||||
* Order in which this page should display on the storefront. (Lower integers specify earlier display.)
|
||||
*/
|
||||
sort_order?: number
|
||||
}
|
||||
|
||||
export type PageTypes = {
|
||||
page: Page
|
||||
/**
|
||||
* Operation to get all pages.
|
||||
*/
|
||||
export type GetAllPagesOperation = {
|
||||
data: { pages: Page[] }
|
||||
}
|
||||
|
||||
export type GetAllPagesOperation<T extends PageTypes = PageTypes> = {
|
||||
data: { pages: T['page'][] }
|
||||
}
|
||||
|
||||
export type GetPageOperation<T extends PageTypes = PageTypes> = {
|
||||
data: { page?: T['page'] }
|
||||
variables: { id: string }
|
||||
export type GetPageOperation = {
|
||||
data: { page?: Page }
|
||||
variables: {
|
||||
/**
|
||||
* The unique identifier of the page.
|
||||
*/
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ export interface ProductPrice {
|
||||
*/
|
||||
value: number
|
||||
/**
|
||||
* Currency code of the product price.
|
||||
* The currency code for the price. This is a 3-letter ISO 4217 code.
|
||||
*/
|
||||
currencyCode?: 'USD' | 'EUR' | 'ARS' | 'GBP' | string
|
||||
/**
|
||||
@ -18,11 +18,11 @@ export interface ProductPrice {
|
||||
export interface ProductOption {
|
||||
__typename?: 'MultipleChoiceOption'
|
||||
/**
|
||||
* The option's id.
|
||||
* The unique identifier for the option.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* The option's name.
|
||||
* The product option’s name.
|
||||
*/
|
||||
displayName: string
|
||||
/**
|
||||
@ -44,17 +44,17 @@ export interface ProductOptionValues {
|
||||
|
||||
export interface ProductVariant {
|
||||
/**
|
||||
* The variant's id.
|
||||
* The unique identifier for the variant.
|
||||
*/
|
||||
id: string | number
|
||||
id: string
|
||||
/**
|
||||
* The SKU (stock keeping unit) associated with the product variant.
|
||||
*/
|
||||
sku: string
|
||||
sku?: string
|
||||
/**
|
||||
* The product variant’s name, or the product's name.
|
||||
*/
|
||||
name: string
|
||||
name?: string
|
||||
/**
|
||||
* List of product options.
|
||||
*/
|
||||
@ -62,11 +62,11 @@ export interface ProductVariant {
|
||||
/**
|
||||
* The product variant’s price after all discounts are applied.
|
||||
*/
|
||||
price: ProductPrice
|
||||
price?: ProductPrice
|
||||
/**
|
||||
* Product variant’s price, as quoted by the manufacturer/distributor.
|
||||
* The retail price of the product. This can be used to mark a product as on sale, when `retailPrice` is higher than the `price`.
|
||||
*/
|
||||
listPrice: ProductPrice
|
||||
retailPrice?: ProductPrice
|
||||
/**
|
||||
* Indicates if the variant is available for sale.
|
||||
*/
|
||||
@ -83,11 +83,11 @@ export interface ProductVariant {
|
||||
|
||||
export interface Product {
|
||||
/**
|
||||
* The product's id.
|
||||
* The unique identifier for the product.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* The product's name.
|
||||
* The name of the product.
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
@ -107,7 +107,7 @@ export interface Product {
|
||||
*/
|
||||
slug?: string
|
||||
/**
|
||||
* A human-friendly string for the product, containing U.
|
||||
* Relative URL on the storefront for the product.
|
||||
*/
|
||||
path?: string
|
||||
/**
|
||||
@ -123,7 +123,7 @@ export interface Product {
|
||||
*/
|
||||
price: ProductPrice
|
||||
/**
|
||||
* The product's price.
|
||||
* List of product's options.
|
||||
*/
|
||||
options: ProductOption[]
|
||||
/**
|
||||
@ -140,13 +140,14 @@ export interface SearchProductsBody {
|
||||
/**
|
||||
* The category ID to filter the products by.
|
||||
*/
|
||||
categoryId?: string | number
|
||||
categoryId?: string
|
||||
/**
|
||||
* The brand ID to filter the products by.
|
||||
*/
|
||||
brandId?: string | number
|
||||
brandId?: string
|
||||
/**
|
||||
* The sort order to sort the products by.
|
||||
* The sort key to sort the products by.
|
||||
* @example 'trending-desc' | 'latest-desc' | 'price-asc' | 'price-desc'
|
||||
*/
|
||||
sort?: string
|
||||
/**
|
||||
|
@ -1,26 +1,34 @@
|
||||
export type SignupBody = {
|
||||
/**
|
||||
* The user's first name.
|
||||
*/
|
||||
firstName: string
|
||||
/**
|
||||
* The user's last name.
|
||||
*/
|
||||
lastName: string
|
||||
/**
|
||||
* The user's email address.
|
||||
*/
|
||||
email: string
|
||||
/**
|
||||
* The user's password.
|
||||
*/
|
||||
password: string
|
||||
}
|
||||
|
||||
export type SignupTypes = {
|
||||
body: SignupBody
|
||||
}
|
||||
|
||||
export type SignupHook<T extends SignupTypes = SignupTypes> = {
|
||||
export interface SignupHook {
|
||||
data: null
|
||||
body: T['body']
|
||||
actionInput: T['body']
|
||||
fetcherInput: T['body']
|
||||
body: SignupBody
|
||||
actionInput: SignupBody
|
||||
fetcherInput: SignupBody
|
||||
}
|
||||
|
||||
export type SignupSchema<T extends SignupTypes = SignupTypes> = {
|
||||
export type SignupSchema = {
|
||||
endpoint: {
|
||||
options: {}
|
||||
handlers: {
|
||||
signup: SignupHook<T>
|
||||
signup: SignupHook
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,51 @@
|
||||
export type Category = {
|
||||
/**
|
||||
* Unique identifier for the category.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* Name of the category.
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* A human-friendly unique string for the category, automatically generated from its name.
|
||||
* @example "t-shirts"
|
||||
*/
|
||||
slug: string
|
||||
/**
|
||||
* Relative URL on the storefront for the category.
|
||||
* @example /t-shirts
|
||||
*/
|
||||
path: string
|
||||
}
|
||||
|
||||
export type Brand = any
|
||||
|
||||
export type SiteTypes = {
|
||||
category: Category
|
||||
brand: Brand
|
||||
export type Brand = {
|
||||
/**
|
||||
* Unique identifier for the brand.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* Name of the brand.
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* A human-friendly unique string for the category, automatically generated from its name.
|
||||
* @example "acme"
|
||||
*/
|
||||
slug: string
|
||||
/**
|
||||
* Relative URL on the storefront for this brand.
|
||||
* @example "/acme"
|
||||
*/
|
||||
path: string
|
||||
}
|
||||
|
||||
export type GetSiteInfoOperation<T extends SiteTypes = SiteTypes> = {
|
||||
/**
|
||||
* Operation to get site information. This includes categories and brands.
|
||||
*/
|
||||
export type GetSiteInfoOperation = {
|
||||
data: {
|
||||
categories: T['category'][]
|
||||
brands: T['brand'][]
|
||||
categories: Category[]
|
||||
brands: Brand[]
|
||||
}
|
||||
}
|
||||
|
@ -1,60 +1,98 @@
|
||||
// TODO: define this type
|
||||
export type Wishlist = any
|
||||
import { Product } from './product'
|
||||
|
||||
export type WishlistItemBody = {
|
||||
variantId: string | number
|
||||
export interface WishlistItem {
|
||||
/**
|
||||
* The unique identifier for the item.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* The unique identifier for the product associated with the wishlist item.
|
||||
*/
|
||||
productId: string
|
||||
/**
|
||||
* The unique identifier for the product variant associated with the wishlist item.
|
||||
*/
|
||||
variantId: string
|
||||
/**
|
||||
* The product associated with the wishlist item.
|
||||
*/
|
||||
product: Product
|
||||
}
|
||||
|
||||
export type WishlistTypes = {
|
||||
wishlist: Wishlist
|
||||
itemBody: WishlistItemBody
|
||||
export interface Wishlist {
|
||||
/**
|
||||
* The unique identifier for the wishlist.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* List of items in the wishlist.
|
||||
*/
|
||||
items: WishlistItem[]
|
||||
|
||||
/**
|
||||
* TODO: Spree provider specific
|
||||
*/
|
||||
token?: string
|
||||
}
|
||||
|
||||
export type GetWishlistHook<T extends WishlistTypes = WishlistTypes> = {
|
||||
data: T['wishlist'] | null
|
||||
export interface WishlistItemBody {
|
||||
/**
|
||||
* The product's variant id.
|
||||
*/
|
||||
variantId: string
|
||||
/**
|
||||
* The product's ID.
|
||||
*/
|
||||
productId: string
|
||||
|
||||
/**
|
||||
* TODO: Spree provider specific
|
||||
*/
|
||||
wishlistToken?: string
|
||||
}
|
||||
|
||||
export interface GetWishlistHook {
|
||||
data: Wishlist | null | undefined
|
||||
body: { includeProducts?: boolean }
|
||||
input: { includeProducts?: boolean }
|
||||
fetcherInput: { customerId: string; includeProducts?: boolean }
|
||||
swrState: { isEmpty: boolean }
|
||||
}
|
||||
|
||||
export type AddItemHook<T extends WishlistTypes = WishlistTypes> = {
|
||||
data: T['wishlist']
|
||||
body: { item: T['itemBody'] }
|
||||
fetcherInput: { item: T['itemBody'] }
|
||||
actionInput: T['itemBody']
|
||||
export interface AddItemHook {
|
||||
data: Wishlist | null | undefined
|
||||
body: { item: WishlistItemBody }
|
||||
fetcherInput: { item: WishlistItemBody }
|
||||
actionInput: WishlistItemBody
|
||||
}
|
||||
|
||||
export type RemoveItemHook<T extends WishlistTypes = WishlistTypes> = {
|
||||
data: T['wishlist'] | null
|
||||
body: { itemId: string }
|
||||
fetcherInput: { itemId: string }
|
||||
export interface RemoveItemHook {
|
||||
data: Wishlist | null | undefined
|
||||
body: { itemId: string; wishlistToken?: string }
|
||||
fetcherInput: { itemId: string; wishlistToken?: string }
|
||||
actionInput: { id: string }
|
||||
input: { wishlist?: { includeProducts?: boolean } }
|
||||
}
|
||||
|
||||
export type WishlistSchema<T extends WishlistTypes = WishlistTypes> = {
|
||||
export type WishlistSchema = {
|
||||
endpoint: {
|
||||
options: {}
|
||||
handlers: {
|
||||
getWishlist: GetWishlistHook<T> & {
|
||||
data: T['wishlist'] | null
|
||||
getWishlist: GetWishlistHook & {
|
||||
data: Wishlist | null
|
||||
body: { customerToken?: string }
|
||||
}
|
||||
addItem: AddItemHook<T> & {
|
||||
addItem: AddItemHook & {
|
||||
body: { customerToken?: string }
|
||||
}
|
||||
removeItem: RemoveItemHook<T> & {
|
||||
removeItem: RemoveItemHook & {
|
||||
body: { customerToken?: string }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type GetCustomerWishlistOperation<
|
||||
T extends WishlistTypes = WishlistTypes
|
||||
> = {
|
||||
data: { wishlist?: T['wishlist'] }
|
||||
export interface GetCustomerWishlistOperation {
|
||||
data: { wishlist?: Wishlist }
|
||||
variables: { customerId: string }
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import type { AddItemHook } from '../types/wishlist'
|
||||
import type { Provider } from '..'
|
||||
|
||||
export type UseAddItem<
|
||||
H extends MutationHook<AddItemHook<any>> = MutationHook<AddItemHook>
|
||||
H extends MutationHook<AddItemHook> = MutationHook<AddItemHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher = mutationFetcher
|
||||
|
@ -5,7 +5,7 @@ import type { RemoveItemHook } from '../types/wishlist'
|
||||
import type { Provider } from '..'
|
||||
|
||||
export type UseRemoveItem<
|
||||
H extends MutationHook<RemoveItemHook<any>> = MutationHook<RemoveItemHook>
|
||||
H extends MutationHook<RemoveItemHook> = MutationHook<RemoveItemHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<RemoveItemHook> = mutationFetcher
|
||||
|
@ -5,7 +5,7 @@ import type { GetWishlistHook } from '../types/wishlist'
|
||||
import type { Provider } from '..'
|
||||
|
||||
export type UseWishlist<
|
||||
H extends SWRHook<GetWishlistHook<any>> = SWRHook<GetWishlistHook>
|
||||
H extends SWRHook<GetWishlistHook> = SWRHook<GetWishlistHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<GetWishlistHook> = SWRFetcher
|
||||
|
@ -1,25 +1,17 @@
|
||||
import type { AddItemHook } from '@vercel/commerce/types/customer/address'
|
||||
import type { MutationHook } from '@vercel/commerce/utils/types'
|
||||
import { useCallback } from 'react'
|
||||
import useAddItem, { UseAddItem } from '@vercel/commerce/customer/address/use-add-item'
|
||||
import { useCheckoutContext } from '@components/checkout/context'
|
||||
import useAddItem, {
|
||||
UseAddItem,
|
||||
} from '@vercel/commerce/customer/address/use-add-item'
|
||||
import { MutationHook } from '@vercel/commerce/utils/types'
|
||||
|
||||
export default useAddItem as UseAddItem<typeof handler>
|
||||
|
||||
export const handler: MutationHook<AddItemHook> = {
|
||||
export const handler: MutationHook<any> = {
|
||||
fetchOptions: {
|
||||
query: '_',
|
||||
method: '_',
|
||||
},
|
||||
useHook: () =>
|
||||
function useHook() {
|
||||
const { setAddressFields } = useCheckoutContext()
|
||||
return useCallback(
|
||||
async function addItem(input) {
|
||||
setAddressFields(input)
|
||||
return undefined
|
||||
},
|
||||
[setAddressFields]
|
||||
)
|
||||
query: '',
|
||||
},
|
||||
async fetcher({ input, options, fetch }) {},
|
||||
useHook:
|
||||
({ fetch }) =>
|
||||
() =>
|
||||
async () => ({}),
|
||||
}
|
||||
|
@ -1,25 +1,17 @@
|
||||
import type { AddItemHook } from '@vercel/commerce/types/customer/card'
|
||||
import type { MutationHook } from '@vercel/commerce/utils/types'
|
||||
import { useCallback } from 'react'
|
||||
import useAddItem, { UseAddItem } from '@vercel/commerce/customer/card/use-add-item'
|
||||
import { useCheckoutContext } from '@components/checkout/context'
|
||||
import useAddItem, {
|
||||
UseAddItem,
|
||||
} from '@vercel/commerce/customer/card/use-add-item'
|
||||
import { MutationHook } from '@vercel/commerce/utils/types'
|
||||
|
||||
export default useAddItem as UseAddItem<typeof handler>
|
||||
|
||||
export const handler: MutationHook<AddItemHook> = {
|
||||
export const handler: MutationHook<any> = {
|
||||
fetchOptions: {
|
||||
url: '_',
|
||||
method: '_',
|
||||
},
|
||||
useHook: () =>
|
||||
function useHook() {
|
||||
const { setCardFields } = useCheckoutContext()
|
||||
return useCallback(
|
||||
async function addItem(input) {
|
||||
setCardFields(input)
|
||||
return undefined
|
||||
},
|
||||
[setCardFields]
|
||||
)
|
||||
query: '',
|
||||
},
|
||||
async fetcher({ input, options, fetch }) {},
|
||||
useHook:
|
||||
({ fetch }) =>
|
||||
() =>
|
||||
async () => ({}),
|
||||
}
|
||||
|
@ -1,9 +1 @@
|
||||
import { LoginBody, LoginTypes } from '@vercel/commerce/types/login'
|
||||
export * from '@vercel/commerce/types/login'
|
||||
|
||||
export type LoginHook<T extends LoginTypes = LoginTypes> = {
|
||||
data: null
|
||||
actionInput: LoginBody
|
||||
fetcherInput: LoginBody
|
||||
body: T['body']
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ function normalizeVariants(
|
||||
if (!Array.isArray(variants)) return []
|
||||
return variants?.map((variant) => ({
|
||||
id: variant.id,
|
||||
sku: variant.sku ?? variant.id,
|
||||
options: Object.entries(variant.options).map(
|
||||
([variantGroupId, variantOptionId]) => {
|
||||
const variantGroupFromId = variantGroups.find(
|
||||
|
@ -1,16 +1,6 @@
|
||||
import type { Wishlist } from '@vercel/commerce/types/wishlist'
|
||||
import { HookFetcher } from '@vercel/commerce/utils/types'
|
||||
|
||||
export type Wishlist = {
|
||||
items: [
|
||||
{
|
||||
product_id: number
|
||||
variant_id: number
|
||||
id: number
|
||||
product: any
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export interface UseWishlistOptions {
|
||||
includeProducts?: boolean
|
||||
}
|
||||
|
@ -1,35 +1,29 @@
|
||||
import type { OperationContext } from '@vercel/commerce/api/operations'
|
||||
import type { KiboCommerceConfig } from '../index'
|
||||
import { getAllPagesQuery } from '../queries/get-all-pages-query'
|
||||
import { GetPagesQueryParams } from "../../types/page";
|
||||
import { normalizePage } from '../../lib/normalize'
|
||||
|
||||
export type GetAllPagesResult<
|
||||
T extends { pages: any[] } = { pages: any[] }
|
||||
> = T
|
||||
export type GetAllPagesResult<T extends { pages: any[] } = { pages: any[] }> = T
|
||||
|
||||
export default function getAllPagesOperation({
|
||||
commerce,
|
||||
}: OperationContext<any>) {
|
||||
|
||||
async function getAllPages({
|
||||
query = getAllPagesQuery,
|
||||
config,
|
||||
variables,
|
||||
}: {
|
||||
url?: string
|
||||
config?: Partial<KiboCommerceConfig>
|
||||
variables?: GetPagesQueryParams
|
||||
preview?: boolean
|
||||
query?: string
|
||||
} = {}): Promise<GetAllPagesResult> {
|
||||
const cfg = commerce.getConfig(config)
|
||||
variables = {
|
||||
documentListName: cfg.documentListName
|
||||
const variables = {
|
||||
documentListName: cfg.documentListName,
|
||||
}
|
||||
const { data } = await cfg.fetch(query, { variables });
|
||||
const { data } = await cfg.fetch(query, { variables })
|
||||
|
||||
const pages = data.documentListDocuments.items.map(normalizePage);
|
||||
const pages = data.documentListDocuments.items.map(normalizePage)
|
||||
|
||||
return { pages }
|
||||
}
|
||||
|
@ -2,14 +2,11 @@ import type {
|
||||
OperationContext,
|
||||
OperationOptions,
|
||||
} from '@vercel/commerce/api/operations'
|
||||
import type {
|
||||
GetCustomerWishlistOperation,
|
||||
Wishlist,
|
||||
} from '@vercel/commerce/types/wishlist'
|
||||
import type { GetCustomerWishlistOperation } from '@vercel/commerce/types/wishlist'
|
||||
// import type { RecursivePartial, RecursiveRequired } from '../utils/types'
|
||||
import { KiboCommerceConfig } from '..'
|
||||
// import getAllProducts, { ProductEdge } from './get-all-products'
|
||||
import {getCustomerWishlistQuery} from '../queries/get-customer-wishlist-query'
|
||||
import { getCustomerWishlistQuery } from '../queries/get-customer-wishlist-query'
|
||||
|
||||
export default function getCustomerWishlistOperation({
|
||||
commerce,
|
||||
@ -40,14 +37,15 @@ export default function getCustomerWishlistOperation({
|
||||
config?: KiboCommerceConfig
|
||||
includeProducts?: boolean
|
||||
}): Promise<T['data']> {
|
||||
let customerWishlist ={}
|
||||
let customerWishlist = {}
|
||||
try {
|
||||
|
||||
config = commerce.getConfig(config)
|
||||
const result= await config?.fetch(getCustomerWishlistQuery,{variables})
|
||||
customerWishlist= result?.data?.customerWishlist;
|
||||
} catch(e) {
|
||||
customerWishlist= {}
|
||||
const result = await config?.fetch(getCustomerWishlistQuery, {
|
||||
variables,
|
||||
})
|
||||
customerWishlist = result?.data?.customerWishlist
|
||||
} catch (e) {
|
||||
customerWishlist = {}
|
||||
}
|
||||
|
||||
return { wishlist: customerWishlist as any }
|
||||
|
@ -1,15 +1,11 @@
|
||||
import type {
|
||||
OperationContext,
|
||||
} from '@vercel/commerce/api/operations'
|
||||
import type { OperationContext } from '@vercel/commerce/api/operations'
|
||||
import type { KiboCommerceConfig, KiboCommerceProvider } from '..'
|
||||
import { normalizePage } from '../../lib/normalize'
|
||||
import { getPageQuery } from '../queries/get-page-query'
|
||||
import type { Page, GetPageQueryParams } from "../../types/page";
|
||||
import type { Page, GetPageOperation } from '../../types/page'
|
||||
import type { Document } from '../../../schema'
|
||||
|
||||
export default function getPageOperation({
|
||||
commerce,
|
||||
}: OperationContext<any>) {
|
||||
export default function getPageOperation({ commerce }: OperationContext<any>) {
|
||||
async function getPage<T extends Page>({
|
||||
url,
|
||||
variables,
|
||||
@ -17,18 +13,21 @@ export default function getPageOperation({
|
||||
preview,
|
||||
}: {
|
||||
url?: string
|
||||
variables: GetPageQueryParams
|
||||
variables: GetPageOperation['variables']
|
||||
config?: Partial<KiboCommerceConfig>
|
||||
preview?: boolean
|
||||
}): Promise<any> {
|
||||
// RecursivePartial forces the method to check for every prop in the data, which is
|
||||
// required in case there's a custom `url`
|
||||
const cfg = commerce.getConfig(config)
|
||||
const pageVariables = { documentListName: cfg.documentListName, filter: `id eq ${variables.id}` }
|
||||
const pageVariables = {
|
||||
documentListName: cfg.documentListName,
|
||||
filter: `id eq ${variables.id}`,
|
||||
}
|
||||
|
||||
const { data } = await cfg.fetch(getPageQuery, { variables: pageVariables })
|
||||
|
||||
const firstPage = data.documentListDocuments.items?.[0];
|
||||
const firstPage = data.documentListDocuments.items?.[0]
|
||||
const page = firstPage as Document
|
||||
if (preview || page?.properties?.is_visible) {
|
||||
return { page: normalizePage(page as any) }
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { PrCategory, CustomerAccountInput, Document } from '../../schema'
|
||||
import { Page } from '../types/page';
|
||||
import { Page } from '../types/page'
|
||||
import { Customer } from '../types/customer'
|
||||
import { WishlistItem } from '@vercel/commerce/types/wishlist'
|
||||
|
||||
export function normalizeProduct(productNode: any, config: any): any {
|
||||
const product = {
|
||||
@ -57,7 +58,7 @@ export function normalizePage(page: Document): Page {
|
||||
url: page.properties.url,
|
||||
body: page.properties.body,
|
||||
is_visible: page.properties.is_visible,
|
||||
sort_order: page.properties.sort_order
|
||||
sort_order: page.properties.sort_order,
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +92,7 @@ export function normalizeCustomer(customer: CustomerAccountInput): Customer {
|
||||
lastName: customer.lastName,
|
||||
email: customer.emailAddress,
|
||||
userName: customer.userName,
|
||||
isAnonymous: customer.isAnonymous
|
||||
isAnonymous: customer.isAnonymous,
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,11 +134,13 @@ export function normalizeCategory(category: PrCategory): any {
|
||||
export function normalizeWishlistItem(
|
||||
item: any,
|
||||
config: any,
|
||||
includeProducts=false
|
||||
): any {
|
||||
includeProducts = false
|
||||
): WishlistItem {
|
||||
if (includeProducts) {
|
||||
return {
|
||||
id: item.id,
|
||||
productId: String(item.product.productCode),
|
||||
variantId: item.product.variationProductCode,
|
||||
product: getProuducts(item, config),
|
||||
}
|
||||
} else {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import * as Core from '@vercel/commerce/types/page'
|
||||
export * from '@vercel/commerce/types/page'
|
||||
|
||||
export type Maybe<T> = T | null
|
||||
export type Scalars = {
|
||||
ID: string
|
||||
@ -13,23 +14,3 @@ export type Scalars = {
|
||||
/** Object custom scalar type */
|
||||
Object: any
|
||||
}
|
||||
|
||||
export * from '@vercel/commerce/types/page'
|
||||
|
||||
export type Page = Core.Page
|
||||
|
||||
export type PageTypes = {
|
||||
page: Page
|
||||
}
|
||||
|
||||
export type GetPagesQueryParams = {
|
||||
documentListName: Maybe<Scalars["String"]>
|
||||
}
|
||||
|
||||
export type GetPageQueryParams = {
|
||||
id: Maybe<Scalars["String"]>
|
||||
documentListName: Maybe<Scalars["String"]>
|
||||
}
|
||||
|
||||
export type GetAllPagesOperation = Core.GetAllPagesOperation<PageTypes>
|
||||
export type GetPageOperation = Core.GetPageOperation<PageTypes>
|
||||
|
@ -4,9 +4,7 @@ import { GetProductOperation } from '@vercel/commerce/types/product'
|
||||
import data from '../../data.json'
|
||||
import type { OperationContext } from '@vercel/commerce/api/operations'
|
||||
|
||||
export default function getProductOperation({
|
||||
commerce,
|
||||
}: OperationContext<any>) {
|
||||
export default function getProductOperation(_p: OperationContext<any>) {
|
||||
async function getProduct<T extends GetProductOperation>({
|
||||
query = '',
|
||||
variables,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { OperationContext } from '@vercel/commerce/api/operations'
|
||||
import { Category } from '@vercel/commerce/types/site'
|
||||
import { Category, GetSiteInfoOperation } from '@vercel/commerce/types/site'
|
||||
import { LocalConfig } from '../index'
|
||||
|
||||
export type GetSiteInfoResult<
|
||||
|
@ -7,7 +7,8 @@
|
||||
"path": "/new-short-sleeve-t-shirt",
|
||||
"slug": "new-short-sleeve-t-shirt",
|
||||
"price": { "value": 25, "currencyCode": "USD" },
|
||||
"descriptionHtml": "<p><span>Show off your love for Next.js and Vercel with this unique, </span><strong>limited edition</strong><span> t-shirt. This design is part of a limited run, numbered drop at the June 2021 Next.js Conf. It features a unique, handcrafted triangle design. Get it while supplies last – only 200 of these shirts will be made! </span><strong>All proceeds will be donated to charity.</strong></p>",
|
||||
"description": "Show off your love for Next.js and Vercel with this unique, limited edition t-shirt. This design is part of a limited run, numbered drop at the June 2021 Next.js Conf. It features a unique, handcrafted triangle design. Get it while supplies last - only 200 of these shirts will be made! All proceeds will be donated to charity.",
|
||||
"descriptionHtml": "<p><span>Show off your love for Next.js and Vercel with this unique, </span><strong>limited edition</strong><span> t-shirt. This design is part of a limited run, numbered drop at the June 2021 Next.js Conf. It features a unique, handcrafted triangle design. Get it while supplies last - only 200 of these shirts will be made! </span><strong>All proceeds will be donated to charity.</strong></p>",
|
||||
"images": [
|
||||
{
|
||||
"url": "/assets/drop-shirt-0.png",
|
||||
@ -31,6 +32,8 @@
|
||||
"variants": [
|
||||
{
|
||||
"id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzU0NDczMjUwMjQ0MjAss=",
|
||||
"name": "New Short Sleeve T-Shirt",
|
||||
"sku": "new-short-sleeve-t-shirt",
|
||||
"options": [
|
||||
{
|
||||
"__typename": "MultipleChoiceOption",
|
||||
@ -80,6 +83,7 @@
|
||||
"path": "/lightweight-jacket",
|
||||
"slug": "lightweight-jacket",
|
||||
"price": { "value": 249.99, "currencyCode": "USD" },
|
||||
"description": "Show off your love for Next.js and Vercel with this unique, limited edition t-shirt. This design is part of a limited run, numbered drop at the June 2021 Next.js Conf. It features a unique, handcrafted triangle design. Get it while supplies last - only 200 of these shirts will be made! All proceeds will be donated to charity.",
|
||||
"descriptionHtml": "<p><span>Show off your love for Next.js and Vercel with this unique, </span><strong>limited edition</strong><span> t-shirt. This design is part of a limited run, numbered drop at the June 2021 Next.js Conf. It features a unique, handcrafted triangle design. Get it while supplies last – only 200 of these shirts will be made! </span><strong>All proceeds will be donated to charity.</strong></p>",
|
||||
"images": [
|
||||
{
|
||||
@ -104,6 +108,8 @@
|
||||
"variants": [
|
||||
{
|
||||
"id": "Z2lkOid8vc2hvcGlmeS9Qcm9kdWN0LzU0NDczMjUwMjQ0MjAss=",
|
||||
"name": "Lightweight Jacket",
|
||||
"sku": "lightweight-jacket",
|
||||
"options": [
|
||||
{
|
||||
"__typename": "MultipleChoiceOption",
|
||||
@ -153,6 +159,7 @@
|
||||
"path": "/shirt",
|
||||
"slug": "shirt",
|
||||
"price": { "value": 25, "currencyCode": "USD" },
|
||||
"description": "Show off your love for Next.js and Vercel with this unique, limited edition t-shirt. This design is part of a limited run, numbered drop at the June 2021 Next.js Conf. It features a unique, handcrafted triangle design. Get it while supplies last - only 200 of these shirts will be made! All proceeds will be donated to charity.",
|
||||
"descriptionHtml": "<p><span>Show off your love for Next.js and Vercel with this unique, </span><strong>limited edition</strong><span> t-shirt. This design is part of a limited run, numbered drop at the June 2021 Next.js Conf. It features a unique, handcrafted triangle design. Get it while supplies last – only 200 of these shirts will be made! </span><strong>All proceeds will be donated to charity.</strong></p>",
|
||||
"images": [
|
||||
{
|
||||
@ -189,6 +196,8 @@
|
||||
"variants": [
|
||||
{
|
||||
"id": "Z2lkOi8vc2hvcGlmeS9Qcms9kdWN0LzU0NDczMjUwMjQ0MjAss=",
|
||||
"name": "Shirt",
|
||||
"sku": "shirt",
|
||||
"options": [
|
||||
{
|
||||
"__typename": "MultipleChoiceOption",
|
||||
|
@ -1,19 +1,8 @@
|
||||
import { HookFetcher } from '@vercel/commerce/utils/types'
|
||||
import type { Product } from '@vercel/commerce/types/product'
|
||||
import { Wishlist } from '@vercel/commerce/types/wishlist'
|
||||
|
||||
const defaultOpts = {}
|
||||
|
||||
export type Wishlist = {
|
||||
items: [
|
||||
{
|
||||
product_id: number
|
||||
variant_id: number
|
||||
id: number
|
||||
product: Product
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export interface UseWishlistOptions {
|
||||
includeProducts?: boolean
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import * as Core from '@vercel/commerce/types/cart'
|
||||
|
||||
export * from '@vercel/commerce/types/cart'
|
||||
|
||||
export interface OrdercloudCart {
|
||||
@ -97,30 +95,3 @@ export interface OrdercloudLineItem {
|
||||
Specs: []
|
||||
xp: null
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend core cart types
|
||||
*/
|
||||
|
||||
export type Cart = Core.Cart & {
|
||||
lineItems: Core.LineItem[]
|
||||
url?: string
|
||||
}
|
||||
|
||||
export type CartTypes = Core.CartTypes
|
||||
|
||||
export type CartHooks = Core.CartHooks<CartTypes>
|
||||
|
||||
export type GetCartHook = CartHooks['getCart']
|
||||
export type AddItemHook = CartHooks['addItem']
|
||||
export type UpdateItemHook = CartHooks['updateItem']
|
||||
export type RemoveItemHook = CartHooks['removeItem']
|
||||
|
||||
export type CartSchema = Core.CartSchema<CartTypes>
|
||||
|
||||
export type CartHandlers = Core.CartHandlers<CartTypes>
|
||||
|
||||
export type GetCartHandler = CartHandlers['getCart']
|
||||
export type AddItemHandler = CartHandlers['addItem']
|
||||
export type UpdateItemHandler = CartHandlers['updateItem']
|
||||
export type RemoveItemHandler = CartHandlers['removeItem']
|
||||
|
@ -1,4 +1 @@
|
||||
import * as Core from '@vercel/commerce/types/checkout'
|
||||
|
||||
export type CheckoutTypes = Core.CheckoutTypes
|
||||
export type CheckoutSchema = Core.CheckoutSchema<CheckoutTypes>
|
||||
export * from '@vercel/commerce/types/checkout'
|
||||
|
@ -1,8 +1,4 @@
|
||||
import * as Core from '@vercel/commerce/types/customer/address'
|
||||
|
||||
export type CustomerAddressTypes = Core.CustomerAddressTypes
|
||||
export type CustomerAddressSchema =
|
||||
Core.CustomerAddressSchema<CustomerAddressTypes>
|
||||
export * from '@vercel/commerce/types/customer/address'
|
||||
|
||||
export interface OrdercloudAddress {
|
||||
ID: string
|
||||
|
@ -1,7 +1,4 @@
|
||||
import * as Core from '@vercel/commerce/types/customer/card'
|
||||
|
||||
export type CustomerCardTypes = Core.CustomerCardTypes
|
||||
export type CustomerCardSchema = Core.CustomerCardSchema<CustomerCardTypes>
|
||||
export * from '@vercel/commerce/types/customer/card'
|
||||
|
||||
export interface OredercloudCreditCard {
|
||||
ID: string
|
||||
|
@ -8,6 +8,7 @@ export function normalize(product: RawProduct): Product {
|
||||
name: product.Name,
|
||||
description: product.Description,
|
||||
slug: product.ID,
|
||||
path: `/${product.ID}`,
|
||||
images: product.xp.Images,
|
||||
price: {
|
||||
value: product.xp.Price,
|
||||
@ -16,6 +17,8 @@ export function normalize(product: RawProduct): Product {
|
||||
variants: product.xp.Variants?.length
|
||||
? product.xp.Variants.map((variant) => ({
|
||||
id: variant.ID,
|
||||
sku: variant.ID,
|
||||
name: product.Name,
|
||||
options: variant.Specs.map((spec) => ({
|
||||
id: spec.SpecID,
|
||||
__typename: 'MultipleChoiceOption',
|
||||
@ -27,12 +30,7 @@ export function normalize(product: RawProduct): Product {
|
||||
],
|
||||
})),
|
||||
}))
|
||||
: [
|
||||
{
|
||||
id: '',
|
||||
options: [],
|
||||
},
|
||||
],
|
||||
: [],
|
||||
options: product.xp.Specs?.length
|
||||
? product.xp.Specs.map((spec) => ({
|
||||
id: spec.ID,
|
||||
|
@ -1,19 +1,8 @@
|
||||
import { HookFetcher } from '@vercel/commerce/utils/types'
|
||||
import type { Product } from '@vercel/commerce/types/product'
|
||||
import type { Wishlist } from '@vercel/commerce/types/wishlist'
|
||||
|
||||
const defaultOpts = {}
|
||||
|
||||
export type Wishlist = {
|
||||
items: [
|
||||
{
|
||||
product_id: number
|
||||
variant_id: number
|
||||
id: number
|
||||
product: Product
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export interface UseWishlistOptions {
|
||||
includeProducts?: boolean
|
||||
}
|
||||
|
@ -6,9 +6,12 @@ import * as Query from '../../utils/queries'
|
||||
|
||||
export type Page = any
|
||||
|
||||
export type GetAllPagesResult<T extends { pages: any[] } = { pages: Page[] }> = T
|
||||
export type GetAllPagesResult<T extends { pages: any[] } = { pages: Page[] }> =
|
||||
T
|
||||
|
||||
export default function getAllPagesOperation({ commerce }: OperationContext<Provider>) {
|
||||
export default function getAllPagesOperation({
|
||||
commerce,
|
||||
}: OperationContext<Provider>) {
|
||||
async function getAllPages({
|
||||
query = Query.PageMany,
|
||||
config,
|
||||
@ -34,11 +37,15 @@ export default function getAllPagesOperation({ commerce }: OperationContext<Prov
|
||||
}
|
||||
)
|
||||
|
||||
const pages = data.pages?.edges?.map(({ node: { title: name, slug, ...node } }: PageCountableEdge) => ({
|
||||
...node,
|
||||
const pages = data?.pages?.edges?.map(
|
||||
({ node: { title: name, slug, ...node } }: PageCountableEdge) =>
|
||||
({
|
||||
id: node.id,
|
||||
url: `/${locale}/${slug}`,
|
||||
body: node.content || '',
|
||||
name,
|
||||
}))
|
||||
} ?? [])
|
||||
)
|
||||
|
||||
return { pages }
|
||||
}
|
||||
|
@ -3,15 +3,16 @@ import { ProductCountableEdge } from '../../../schema'
|
||||
import type { Provider, SaleorConfig } from '..'
|
||||
|
||||
import { getAllProductsPathsQuery } from '../../utils/queries'
|
||||
import fetchAllProducts from '../utils/fetch-all-products'
|
||||
|
||||
export type GetAllProductPathsResult = {
|
||||
products: Array<{ path: string }>
|
||||
}
|
||||
|
||||
export default function getAllProductPathsOperation({ commerce }: OperationContext<Provider>) {
|
||||
export default function getAllProductPathsOperation({
|
||||
commerce,
|
||||
}: OperationContext<Provider>) {
|
||||
async function getAllProductPaths({
|
||||
query,
|
||||
query = getAllProductsPathsQuery,
|
||||
config,
|
||||
variables,
|
||||
}: {
|
||||
@ -21,16 +22,15 @@ export default function getAllProductPathsOperation({ commerce }: OperationConte
|
||||
} = {}): Promise<GetAllProductPathsResult> {
|
||||
config = commerce.getConfig(config)
|
||||
|
||||
const products = await fetchAllProducts({
|
||||
config,
|
||||
query: getAllProductsPathsQuery,
|
||||
variables,
|
||||
})
|
||||
const { data }: any = await config.fetch(query, { variables })
|
||||
|
||||
return {
|
||||
products: products?.map(({ node: { slug } }: ProductCountableEdge) => ({
|
||||
products: data?.products?.edges?.map(
|
||||
({ node: { slug } }: ProductCountableEdge) =>
|
||||
({
|
||||
path: `/${slug}`,
|
||||
})),
|
||||
} ?? [])
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,9 @@ type ReturnType = {
|
||||
products: Product[]
|
||||
}
|
||||
|
||||
export default function getAllProductsOperation({ commerce }: OperationContext<Provider>) {
|
||||
export default function getAllProductsOperation({
|
||||
commerce,
|
||||
}: OperationContext<Provider>) {
|
||||
async function getAllProducts({
|
||||
query = Query.ProductMany,
|
||||
variables,
|
||||
@ -46,13 +48,18 @@ export default function getAllProductsOperation({ commerce }: OperationContext<P
|
||||
|
||||
if (featured) {
|
||||
const products =
|
||||
data.collection.products?.edges?.map(({ node: p }: ProductCountableEdge) => normalizeProduct(p)) ?? []
|
||||
data?.collection.products?.edges?.map(
|
||||
({ node: p }: ProductCountableEdge) => normalizeProduct(p)
|
||||
) ?? []
|
||||
|
||||
return {
|
||||
products,
|
||||
}
|
||||
} else {
|
||||
const products = data.products?.edges?.map(({ node: p }: ProductCountableEdge) => normalizeProduct(p)) ?? []
|
||||
const products =
|
||||
data?.products?.edges?.map(({ node: p }: ProductCountableEdge) =>
|
||||
normalizeProduct(p)
|
||||
) ?? []
|
||||
|
||||
return {
|
||||
products,
|
||||
|
@ -8,7 +8,9 @@ export type Page = any
|
||||
|
||||
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({
|
||||
query = Query.PageOne,
|
||||
variables,
|
||||
@ -21,9 +23,7 @@ export default function getPageOperation({ commerce }: OperationContext<Provider
|
||||
}): Promise<GetPageResult> {
|
||||
const { fetch, locale = 'en-US' } = commerce.getConfig(config)
|
||||
|
||||
const {
|
||||
data: { page },
|
||||
} = await fetch(
|
||||
const { data } = await fetch(
|
||||
query,
|
||||
{ variables },
|
||||
{
|
||||
@ -36,11 +36,13 @@ export default function getPageOperation({ commerce }: OperationContext<Provider
|
||||
)
|
||||
|
||||
return {
|
||||
page: page
|
||||
page:
|
||||
data && data.page
|
||||
? {
|
||||
...page,
|
||||
name: page.title,
|
||||
url: `/${locale}/${page.slug}`,
|
||||
...data.page,
|
||||
name: data.page.title,
|
||||
body: data.page.content || '',
|
||||
url: `/${locale}/${data.page.slug}`,
|
||||
}
|
||||
: null,
|
||||
}
|
||||
|
@ -12,7 +12,9 @@ type ReturnType = {
|
||||
product: any
|
||||
}
|
||||
|
||||
export default function getProductOperation({ commerce }: OperationContext<Provider>) {
|
||||
export default function getProductOperation({
|
||||
commerce,
|
||||
}: OperationContext<Provider>) {
|
||||
async function getProduct({
|
||||
query = Query.ProductOneBySlug,
|
||||
variables,
|
||||
@ -38,7 +40,7 @@ export default function getProductOperation({ commerce }: OperationContext<Provi
|
||||
)
|
||||
|
||||
return {
|
||||
product: data?.product ? normalizeProduct(data.product) : null,
|
||||
product: data && data.product ? normalizeProduct(data.product) : null,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
import { ProductCountableEdge } from '../../../schema'
|
||||
import { SaleorConfig } from '..'
|
||||
|
||||
const fetchAllProducts = async ({
|
||||
config,
|
||||
query,
|
||||
variables,
|
||||
acc = [],
|
||||
cursor,
|
||||
}: {
|
||||
config: SaleorConfig
|
||||
query: string
|
||||
acc?: ProductCountableEdge[]
|
||||
variables?: any
|
||||
cursor?: string
|
||||
}): Promise<ProductCountableEdge[]> => {
|
||||
const { data } = await config.fetch(query, {
|
||||
variables: { ...variables, cursor },
|
||||
})
|
||||
|
||||
const edges: ProductCountableEdge[] = data.products?.edges ?? []
|
||||
const hasNextPage = data.products?.pageInfo?.hasNextPage
|
||||
acc = acc.concat(edges)
|
||||
|
||||
if (hasNextPage) {
|
||||
const cursor = edges.pop()?.cursor
|
||||
if (cursor) {
|
||||
return fetchAllProducts({
|
||||
config,
|
||||
query,
|
||||
variables,
|
||||
acc,
|
||||
cursor,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return acc
|
||||
}
|
||||
|
||||
export default fetchAllProducts
|
@ -38,7 +38,7 @@ export const handler: SWRHook<SearchProductsHook> = {
|
||||
let edges
|
||||
|
||||
if (categoryId) {
|
||||
edges = data.collection?.products?.edges ?? []
|
||||
edges = data?.collection?.products?.edges ?? []
|
||||
// FIXME @zaiste, no `vendor` in Saleor
|
||||
// if (brandId) {
|
||||
// edges = edges.filter(
|
||||
@ -47,11 +47,13 @@ export const handler: SWRHook<SearchProductsHook> = {
|
||||
// )
|
||||
// }
|
||||
} else {
|
||||
edges = data.products?.edges ?? []
|
||||
edges = data?.products?.edges ?? []
|
||||
}
|
||||
|
||||
return {
|
||||
products: edges.map(({ node }: ProductCountableEdge) => normalizeProduct(node)),
|
||||
products: edges.map(({ node }: ProductCountableEdge) =>
|
||||
normalizeProduct(node)
|
||||
),
|
||||
found: !!edges.length,
|
||||
}
|
||||
},
|
||||
|
@ -6,17 +6,19 @@ import * as query from './queries'
|
||||
const getCategories = async (config: SaleorConfig): Promise<Category[]> => {
|
||||
const { data } = await config.fetch(query.CollectionMany, {
|
||||
variables: {
|
||||
first: 100,
|
||||
first: 50,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
data.collections?.edges?.map(({ node: { id, name, slug } }: CollectionCountableEdge) => ({
|
||||
data?.collections?.edges?.map(
|
||||
({ node: { id, name, slug } }: CollectionCountableEdge) => ({
|
||||
id,
|
||||
name,
|
||||
slug,
|
||||
path: `/${slug}`,
|
||||
})) ?? []
|
||||
})
|
||||
) ?? []
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ const getVendors = async (config: SaleorConfig): Promise<BrandEdge[]> => {
|
||||
// node: {
|
||||
// entityId: id,
|
||||
// name: v,
|
||||
// path: `brands/${id}`,
|
||||
// path: `/${id}`,
|
||||
// },
|
||||
// }
|
||||
// })
|
||||
|
@ -1,6 +1,12 @@
|
||||
import { Product } from '@vercel/commerce/types/product'
|
||||
|
||||
import { Product as SaleorProduct, Checkout, CheckoutLine, Money, ProductVariant } from '../../schema'
|
||||
import {
|
||||
Product as SaleorProduct,
|
||||
Checkout,
|
||||
CheckoutLine,
|
||||
Money,
|
||||
ProductVariant,
|
||||
} from '../../schema'
|
||||
|
||||
import type { Cart, LineItem } from '../types'
|
||||
|
||||
@ -19,11 +25,14 @@ const normalizeProductOptions = (options: ProductVariant[]) => {
|
||||
?.map((option) => option?.attributes)
|
||||
.flat(1)
|
||||
.reduce<any>((acc, x) => {
|
||||
if (acc.find(({ displayName }: any) => displayName === x.attribute.name)) {
|
||||
if (
|
||||
acc.find(({ displayName }: any) => displayName === x.attribute.name)
|
||||
) {
|
||||
return acc.map((opt: any) => {
|
||||
return opt.displayName === x.attribute.name
|
||||
? {
|
||||
...opt,
|
||||
id: x.attribute.id,
|
||||
values: [
|
||||
...opt.values,
|
||||
...x.values.map((value: any) => ({
|
||||
@ -37,6 +46,7 @@ const normalizeProductOptions = (options: ProductVariant[]) => {
|
||||
|
||||
return acc.concat({
|
||||
__typename: 'MultipleChoiceOption',
|
||||
id: x.attribute.id,
|
||||
displayName: x.attribute.name,
|
||||
variant: 'size',
|
||||
values: x.values.map((value: any) => ({
|
||||
@ -54,7 +64,7 @@ const normalizeProductVariants = (variants: ProductVariant[]) => {
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
sku: sku ?? id,
|
||||
...(!!sku && { sku }),
|
||||
price,
|
||||
listPrice: price,
|
||||
requiresShipping: true,
|
||||
@ -64,23 +74,41 @@ const normalizeProductVariants = (variants: ProductVariant[]) => {
|
||||
}
|
||||
|
||||
export function normalizeProduct(productNode: SaleorProduct): Product {
|
||||
const { id, name, media = [], variants, description, slug, pricing, ...rest } = productNode
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
media = [],
|
||||
variants,
|
||||
description,
|
||||
slug,
|
||||
pricing,
|
||||
...rest
|
||||
} = productNode
|
||||
|
||||
const product = {
|
||||
id,
|
||||
name,
|
||||
vendor: '',
|
||||
description: description ? JSON.parse(description)?.blocks[0]?.data.text : '',
|
||||
description: description
|
||||
? JSON.parse(description)?.blocks[0]?.data.text
|
||||
: '',
|
||||
path: `/${slug}`,
|
||||
slug: slug?.replace(/^\/+|\/+$/g, ''),
|
||||
price: (pricing?.priceRange?.start?.net && money(pricing.priceRange.start.net)) || {
|
||||
price: (pricing?.priceRange?.start?.net &&
|
||||
money(pricing.priceRange.start.net)) || {
|
||||
value: 0,
|
||||
currencyCode: 'USD',
|
||||
},
|
||||
// TODO: Check nextjs-commerce bug if no images are added for a product
|
||||
images: media?.length ? media : [{ url: placeholderImg }],
|
||||
variants: variants && variants.length > 0 ? normalizeProductVariants(variants as ProductVariant[]) : [],
|
||||
options: variants && variants.length > 0 ? normalizeProductOptions(variants as ProductVariant[]) : [],
|
||||
variants:
|
||||
variants && variants.length > 0
|
||||
? normalizeProductVariants(variants as ProductVariant[])
|
||||
: [],
|
||||
options:
|
||||
variants && variants.length > 0
|
||||
? normalizeProductOptions(variants as ProductVariant[])
|
||||
: [],
|
||||
...rest,
|
||||
}
|
||||
|
||||
@ -89,7 +117,8 @@ export function normalizeProduct(productNode: SaleorProduct): Product {
|
||||
|
||||
export function normalizeCart(checkout: Checkout): Cart {
|
||||
const lines = checkout.lines as CheckoutLine[]
|
||||
const lineItems: LineItem[] = lines.length > 0 ? lines?.map<LineItem>(normalizeLineItem) : []
|
||||
const lineItems: LineItem[] =
|
||||
lines.length > 0 ? lines?.map<LineItem>(normalizeLineItem) : []
|
||||
|
||||
return {
|
||||
id: checkout.id,
|
||||
@ -117,7 +146,7 @@ function normalizeLineItem({ id, variant, quantity }: CheckoutLine): LineItem {
|
||||
quantity,
|
||||
variant: {
|
||||
id: String(variant?.id),
|
||||
sku: variant?.sku ?? '',
|
||||
...(variant?.sku && { sku: variant.sku }),
|
||||
name: variant?.name!,
|
||||
image: {
|
||||
url: variant?.media![0] ? variant?.media![0].url : placeholderImg,
|
||||
|
@ -1,7 +1,11 @@
|
||||
import * as fragment from '../fragments'
|
||||
|
||||
export const CollectionOne = /* GraphQL */ `
|
||||
query getProductsFromCollection($categoryId: ID!, $first: Int = 100, $channel: String = "default-channel") {
|
||||
query getProductsFromCollection(
|
||||
$categoryId: ID!
|
||||
$first: Int = 50
|
||||
$channel: String = "default-channel"
|
||||
) {
|
||||
collection(id: $categoryId, channel: $channel) {
|
||||
id
|
||||
products(first: $first) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
export const getAllProductVendors = /* GraphQL */ `
|
||||
query getAllProductVendors($first: Int = 250, $cursor: String) {
|
||||
query getAllProductVendors($first: Int = 50, $cursor: String) {
|
||||
products(first: $first, after: $cursor) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
|
@ -1,5 +1,9 @@
|
||||
export const getAllProductsPathsQuery = /* GraphQL */ `
|
||||
query getAllProductPaths($first: Int = 100, $cursor: String, $channel: String = "default-channel") {
|
||||
query getAllProductPaths(
|
||||
$first: Int = 50
|
||||
$cursor: String
|
||||
$channel: String = "default-channel"
|
||||
) {
|
||||
products(first: $first, after: $cursor, channel: $channel) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
|
@ -1,5 +1,5 @@
|
||||
export const PageMany = /* GraphQL */ `
|
||||
query PageMany($first: Int = 100) {
|
||||
query PageMany($first: Int = 50) {
|
||||
pages(first: $first) {
|
||||
edges {
|
||||
node {
|
||||
|
@ -2,12 +2,17 @@ import * as fragment from '../fragments'
|
||||
|
||||
export const ProductMany = /* GraphQL */ `
|
||||
query ProductMany(
|
||||
$first: Int = 100
|
||||
$first: Int = 50
|
||||
$filter: ProductFilterInput
|
||||
$sortBy: ProductOrder
|
||||
$channel: String = "default-channel"
|
||||
) {
|
||||
products(first: $first, channel: $channel, filter: $filter, sortBy: $sortBy) {
|
||||
products(
|
||||
first: $first
|
||||
channel: $channel
|
||||
filter: $filter
|
||||
sortBy: $sortBy
|
||||
) {
|
||||
...ProductConnection
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ export const ProductOneBySlug = /* GraphQL */ `
|
||||
name
|
||||
attributes {
|
||||
attribute {
|
||||
id
|
||||
name
|
||||
}
|
||||
values {
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { Product as SFCCProduct, Search } from 'commerce-sdk'
|
||||
import type {
|
||||
Product,
|
||||
ProductImage,
|
||||
ProductOption,
|
||||
ProductVariant,
|
||||
} from '@vercel/commerce/types/product'
|
||||
@ -64,9 +63,9 @@ export function normalizeProduct(
|
||||
currencyCode: product.currency,
|
||||
},
|
||||
images: product.imageGroups![0].images.map((image) => ({
|
||||
url: image.disBaseLink,
|
||||
altText: image.title,
|
||||
})) as ProductImage[],
|
||||
url: image.disBaseLink || image.link,
|
||||
alt: image.title || '',
|
||||
})),
|
||||
variants: normaliseVariants(product.variants),
|
||||
options: normaliseOptions(product.variationAttributes),
|
||||
}
|
||||
@ -87,8 +86,8 @@ export function normalizeSearchProducts(
|
||||
images: [
|
||||
{
|
||||
url: product.image!.link,
|
||||
altText: product.productName,
|
||||
} as ProductImage,
|
||||
alt: product.productName,
|
||||
},
|
||||
],
|
||||
variants: normaliseVariants(product.variants),
|
||||
options: normaliseOptions(product.variationAttributes),
|
||||
|
@ -1 +0,0 @@
|
||||
export default function (_commerce: any) {}
|
@ -0,0 +1,26 @@
|
||||
import { ProductsEndpoint } from '.'
|
||||
|
||||
const SORT: { [key: string]: string | undefined } = {
|
||||
latest: 'id',
|
||||
trending: 'total_sold',
|
||||
price: 'price',
|
||||
}
|
||||
|
||||
const LIMIT = 12
|
||||
|
||||
// Return current cart info
|
||||
const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
|
||||
res,
|
||||
body: { search, categoryId, brandId, sort },
|
||||
config,
|
||||
commerce,
|
||||
}) => {
|
||||
res.status(200).json({
|
||||
data: {
|
||||
products: [],
|
||||
found: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export default getProducts
|
19
packages/shopify/src/api/endpoints/catalog/products/index.ts
Normal file
19
packages/shopify/src/api/endpoints/catalog/products/index.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
|
||||
import productsEndpoint from '@vercel/commerce/api/endpoints/catalog/products'
|
||||
import type { ProductsSchema } from '@vercel/commerce/types/product'
|
||||
import type { ShopifyAPI } from '../../..'
|
||||
|
||||
import getProducts from './get-products'
|
||||
|
||||
export type ProductsAPI = GetAPISchema<ShopifyAPI, ProductsSchema>
|
||||
|
||||
export type ProductsEndpoint = ProductsAPI['endpoint']
|
||||
|
||||
export const handlers: ProductsEndpoint['handlers'] = { getProducts }
|
||||
|
||||
const productsApi = createEndpoint<ProductsAPI>({
|
||||
handler: productsEndpoint,
|
||||
handlers,
|
||||
})
|
||||
|
||||
export default productsApi
|
@ -13,30 +13,30 @@ import {
|
||||
export default function getProductOperation({
|
||||
commerce,
|
||||
}: OperationContext<Provider>) {
|
||||
async function getProduct(opts: {
|
||||
variables: GetProductOperation['variables']
|
||||
async function getProduct<T extends GetProductOperation>(opts: {
|
||||
variables: T['variables']
|
||||
config?: Partial<ShopifyConfig>
|
||||
preview?: boolean
|
||||
}): Promise<GetProductOperation['data']>
|
||||
}): Promise<T['data']>
|
||||
|
||||
async function getProduct(
|
||||
async function getProduct<T extends GetProductOperation>(
|
||||
opts: {
|
||||
variables: GetProductOperation['variables']
|
||||
variables: T['variables']
|
||||
config?: Partial<ShopifyConfig>
|
||||
preview?: boolean
|
||||
} & OperationOptions
|
||||
): Promise<GetProductOperation['data']>
|
||||
): Promise<T['data']>
|
||||
|
||||
async function getProduct({
|
||||
async function getProduct<T extends GetProductOperation>({
|
||||
query = getProductQuery,
|
||||
variables,
|
||||
config: cfg,
|
||||
}: {
|
||||
query?: string
|
||||
variables: GetProductOperation['variables']
|
||||
variables: T['variables']
|
||||
config?: Partial<ShopifyConfig>
|
||||
preview?: boolean
|
||||
}): Promise<GetProductOperation['data']> {
|
||||
}): Promise<T['data']> {
|
||||
const { fetch, locale } = commerce.getConfig(cfg)
|
||||
|
||||
const {
|
||||
|
@ -6,7 +6,7 @@ import { GetSiteInfoQueryVariables } from '../../../schema'
|
||||
import type { ShopifyConfig, Provider } from '..'
|
||||
import { GetSiteInfoOperation } from '../../types/site'
|
||||
|
||||
import { getCategories, getBrands, getSiteInfoQuery } from '../../utils'
|
||||
import { getCategories, getBrands } from '../../utils'
|
||||
|
||||
export default function getSiteInfoOperation({
|
||||
commerce,
|
||||
@ -24,9 +24,7 @@ export default function getSiteInfoOperation({
|
||||
): Promise<T['data']>
|
||||
|
||||
async function getSiteInfo<T extends GetSiteInfoOperation>({
|
||||
query = getSiteInfoQuery,
|
||||
config,
|
||||
variables,
|
||||
}: {
|
||||
query?: string
|
||||
config?: Partial<ShopifyConfig>
|
||||
@ -37,24 +35,15 @@ export default function getSiteInfoOperation({
|
||||
|
||||
const categoriesPromise = getCategories(cfg)
|
||||
const brandsPromise = getBrands(cfg)
|
||||
/*
|
||||
const { fetch, locale } = cfg
|
||||
const { data } = await fetch<GetSiteInfoQuery, GetSiteInfoQueryVariables>(
|
||||
query,
|
||||
{ variables },
|
||||
{
|
||||
...(locale && {
|
||||
headers: {
|
||||
'Accept-Language': locale,
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
*/
|
||||
|
||||
const [categories, brands] = await Promise.all([
|
||||
categoriesPromise,
|
||||
brandsPromise,
|
||||
])
|
||||
|
||||
return {
|
||||
categories: await categoriesPromise,
|
||||
brands: await brandsPromise,
|
||||
categories,
|
||||
brands,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ const fetchGraphqlApi: GraphQLFetcher = async (
|
||||
throw getError(
|
||||
[
|
||||
{
|
||||
message: `${err} \n Most likely related to an unexpected output. e.g the store might be protected with password or not available.`,
|
||||
message: `${err} \n Most likely related to an unexpected output. E.g: NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN & NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN might be incorect.`,
|
||||
},
|
||||
],
|
||||
500
|
||||
|
@ -1,5 +1 @@
|
||||
import * as Core from '@vercel/commerce/types/customer'
|
||||
|
||||
export * from '@vercel/commerce/types/customer'
|
||||
|
||||
export type CustomerSchema = Core.CustomerSchema
|
||||
|
@ -1,8 +1 @@
|
||||
import * as Core from '@vercel/commerce/types/login'
|
||||
import type { CustomerAccessTokenCreateInput } from '../../schema'
|
||||
|
||||
export * from '@vercel/commerce/types/login'
|
||||
|
||||
export type LoginOperation = Core.LoginOperation & {
|
||||
variables: CustomerAccessTokenCreateInput
|
||||
}
|
||||
|
@ -1,11 +1 @@
|
||||
import * as Core from '@vercel/commerce/types/page'
|
||||
export * from '@vercel/commerce/types/page'
|
||||
|
||||
export type Page = Core.Page
|
||||
|
||||
export type PageTypes = {
|
||||
page: Page
|
||||
}
|
||||
|
||||
export type GetAllPagesOperation = Core.GetAllPagesOperation<PageTypes>
|
||||
export type GetPageOperation = Core.GetPageOperation<PageTypes>
|
||||
|
@ -5,19 +5,7 @@ import {
|
||||
import { ShopifyConfig } from '../api'
|
||||
import getAllProductVendors from './queries/get-all-product-vendors-query'
|
||||
|
||||
export type Brand = {
|
||||
entityId: string
|
||||
name: string
|
||||
path: string
|
||||
}
|
||||
|
||||
export type BrandEdge = {
|
||||
node: Brand
|
||||
}
|
||||
|
||||
export type Brands = BrandEdge[]
|
||||
|
||||
const getBrands = async (config: ShopifyConfig): Promise<BrandEdge[]> => {
|
||||
const getBrands = async (config: ShopifyConfig) => {
|
||||
const { data } = await config.fetch<
|
||||
GetAllProductVendorsQuery,
|
||||
GetAllProductVendorsQueryVariables
|
||||
@ -32,11 +20,10 @@ const getBrands = async (config: ShopifyConfig): Promise<BrandEdge[]> => {
|
||||
return [...new Set(vendorsStrings)].map((v) => {
|
||||
const id = v.replace(/\s+/g, '-').toLowerCase()
|
||||
return {
|
||||
node: {
|
||||
entityId: id,
|
||||
id,
|
||||
name: v,
|
||||
path: `brands/${id}`,
|
||||
},
|
||||
slug: id,
|
||||
path: `/${id}`,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ const normalizeProductVariants = ({ edges }: ProductVariantConnection) => {
|
||||
return {
|
||||
id,
|
||||
name: title,
|
||||
sku: sku ?? id,
|
||||
sku,
|
||||
price: +priceV2.amount,
|
||||
listPrice: +compareAtPriceV2?.amount,
|
||||
requiresShipping,
|
||||
@ -180,6 +180,7 @@ export const normalizePage = (
|
||||
...page,
|
||||
url: `/${locale}/${handle}`,
|
||||
name,
|
||||
body: page.body ?? '',
|
||||
})
|
||||
|
||||
export const normalizePages = (edges: PageEdge[], locale?: string): Page[] =>
|
||||
|
@ -120,11 +120,10 @@ export default function getSiteInfoOperation({
|
||||
.sort(taxonsSort)
|
||||
.map((spreeTaxon: TaxonAttr) => {
|
||||
return {
|
||||
node: {
|
||||
entityId: spreeTaxon.id,
|
||||
path: `brands/${spreeTaxon.id}`,
|
||||
id: spreeTaxon.id,
|
||||
path: `/${spreeTaxon.id}`,
|
||||
slug: spreeTaxon.id,
|
||||
name: spreeTaxon.attributes.name,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -8,10 +8,9 @@ import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfac
|
||||
import type { Response } from '@vercel/fetch'
|
||||
import type { ProductOption, Product } from '@vercel/commerce/types/product'
|
||||
import type {
|
||||
AddItemHook,
|
||||
RemoveItemHook,
|
||||
WishlistItemBody,
|
||||
WishlistTypes,
|
||||
Wishlist as CoreWishlist,
|
||||
WishlistItemBody as CoreWishlistItemBody,
|
||||
RemoveItemHook as CoreRemoveItemHook,
|
||||
} from '@vercel/commerce/types/wishlist'
|
||||
|
||||
export type UnknownObjectValues = Record<string, unknown>
|
||||
@ -134,31 +133,22 @@ export type UserOAuthTokens = {
|
||||
accessToken: string
|
||||
}
|
||||
|
||||
// TODO: ExplicitCommerceWishlist is a temporary type
|
||||
// derived from tsx views. It will be removed once
|
||||
// Wishlist in @vercel/commerce/types/wishlist is updated
|
||||
// to a more specific type than `any`.
|
||||
export type ExplicitCommerceWishlist = {
|
||||
id: string
|
||||
export interface Wishlist extends CoreWishlist {
|
||||
token: string
|
||||
items: {
|
||||
id: string
|
||||
product_id: number
|
||||
variant_id: number
|
||||
product: Product
|
||||
}[]
|
||||
}
|
||||
|
||||
export type ExplicitWishlistAddItemHook = AddItemHook<
|
||||
WishlistTypes & {
|
||||
wishlist: ExplicitCommerceWishlist
|
||||
itemBody: WishlistItemBody & {
|
||||
wishlistToken?: string
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
export type ExplicitWishlistRemoveItemHook = RemoveItemHook & {
|
||||
fetcherInput: { wishlistToken?: string }
|
||||
body: { wishlistToken?: string }
|
||||
export interface WishlistItemBody extends CoreWishlistItemBody {
|
||||
wishlistToken: string
|
||||
}
|
||||
|
||||
export type AddItemHook = {
|
||||
data: Wishlist | null | undefined
|
||||
body: { item: WishlistItemBody }
|
||||
fetcherInput: { item: WishlistItemBody }
|
||||
actionInput: WishlistItemBody
|
||||
}
|
||||
|
||||
export type RemoveItemHook = CoreRemoveItemHook & {
|
||||
fetcherInput: { itemId: string; wishlistToken?: string }
|
||||
body: { temId: string; wishlistToken?: string }
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Based on https://github.com/spark-solutions/spree2vuestorefront/blob/d88d85ae1bcd2ec99b13b81cd2e3c25600a0216e/src/utils/index.ts
|
||||
|
||||
import type { ProductImage } from '@vercel/commerce/types/product'
|
||||
import type { Image } from '@vercel/commerce/types/common'
|
||||
import type { SpreeProductImage } from '../types'
|
||||
|
||||
const getMediaGallery = (
|
||||
@ -11,7 +11,7 @@ const getMediaGallery = (
|
||||
minHeight: number
|
||||
) => string | null
|
||||
) => {
|
||||
return images.reduce<ProductImage[]>((productImages, _, imageIndex) => {
|
||||
return images.reduce<Image[]>((productImages, _, imageIndex) => {
|
||||
const url = getImageUrl(images[imageIndex], 800, 800)
|
||||
|
||||
if (url) {
|
||||
|
@ -88,7 +88,6 @@ const normalizeVariant = (
|
||||
price: parseFloat(spreeVariant.attributes.price),
|
||||
listPrice: parseFloat(spreeVariant.attributes.price),
|
||||
image,
|
||||
isInStock: spreeVariant.attributes.in_stock,
|
||||
availableForSale: spreeVariant.attributes.purchasable,
|
||||
...(spreeVariant.attributes.weight === '0.0'
|
||||
? {}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type {
|
||||
Product,
|
||||
ProductImage,
|
||||
ProductPrice,
|
||||
ProductVariant,
|
||||
} from '@vercel/commerce/types/product'
|
||||
import type { Image } from '@vercel/commerce/types/common'
|
||||
import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product'
|
||||
import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships'
|
||||
import { jsonApi } from '@spree/storefront-api-v2-sdk'
|
||||
@ -105,6 +105,7 @@ const normalizeProduct = (
|
||||
|
||||
return {
|
||||
id: spreeVariantRecord.id,
|
||||
sku: spreeVariantRecord.attributes.sku || spreeVariantRecord.id,
|
||||
options: variantOptions,
|
||||
}
|
||||
}
|
||||
@ -213,7 +214,7 @@ const normalizeProduct = (
|
||||
createGetAbsoluteImageUrl(requireConfigValue('imageHost') as string)
|
||||
)
|
||||
|
||||
const images: ProductImage[] =
|
||||
const images: Image[] =
|
||||
productImages.length === 0
|
||||
? placeholderImage === false
|
||||
? []
|
||||
|
@ -4,25 +4,21 @@ import { jsonApi } from '@spree/storefront-api-v2-sdk'
|
||||
import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product'
|
||||
import type { WishedItemAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/WishedItem'
|
||||
import type { WishlistAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Wishlist'
|
||||
import type {
|
||||
ExplicitCommerceWishlist,
|
||||
SpreeSdkResponse,
|
||||
VariantAttr,
|
||||
} from '../../types'
|
||||
import type { SpreeSdkResponse, VariantAttr } from '../../types'
|
||||
import normalizeProduct from './normalize-product'
|
||||
import { Wishlist } from '@vercel/commerce/types/wishlist'
|
||||
|
||||
const normalizeWishlist = (
|
||||
spreeSuccessResponse: SpreeSdkResponse,
|
||||
spreeWishlist: WishlistAttr
|
||||
): ExplicitCommerceWishlist => {
|
||||
): Wishlist => {
|
||||
const spreeWishedItems = jsonApi.findRelationshipDocuments<WishedItemAttr>(
|
||||
spreeSuccessResponse,
|
||||
spreeWishlist,
|
||||
'wished_items'
|
||||
)
|
||||
|
||||
const items: ExplicitCommerceWishlist['items'] = spreeWishedItems.map(
|
||||
(spreeWishedItem) => {
|
||||
const items: Wishlist['items'] = spreeWishedItems.map((spreeWishedItem) => {
|
||||
const spreeWishedVariant =
|
||||
jsonApi.findSingleRelationshipDocument<VariantAttr>(
|
||||
spreeSuccessResponse,
|
||||
@ -51,12 +47,11 @@ const normalizeWishlist = (
|
||||
|
||||
return {
|
||||
id: spreeWishedItem.id,
|
||||
product_id: parseInt(spreeWishedProduct.id, 10),
|
||||
variant_id: parseInt(spreeWishedVariant.id, 10),
|
||||
productId: spreeWishedProduct.id,
|
||||
variantId: spreeWishedVariant.id,
|
||||
product: normalizeProduct(spreeSuccessResponse, spreeWishedProduct),
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return {
|
||||
id: spreeWishlist.id,
|
||||
|
@ -2,8 +2,9 @@ import { useCallback } from 'react'
|
||||
import type { MutationHook } from '@vercel/commerce/utils/types'
|
||||
import useAddItem from '@vercel/commerce/wishlist/use-add-item'
|
||||
import type { UseAddItem } from '@vercel/commerce/wishlist/use-add-item'
|
||||
import type { AddItemHook } from '@vercel/commerce/types/wishlist'
|
||||
import useWishlist from './use-wishlist'
|
||||
import type { ExplicitWishlistAddItemHook } from '../types'
|
||||
|
||||
import type {
|
||||
WishedItem,
|
||||
WishlistsAddWishedItem,
|
||||
@ -11,12 +12,12 @@ import type {
|
||||
import type { GraphQLFetcherResult } from '@vercel/commerce/api'
|
||||
import ensureIToken from '../utils/tokens/ensure-itoken'
|
||||
import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token'
|
||||
import type { AddItemHook } from '@vercel/commerce/types/wishlist'
|
||||
|
||||
import isLoggedIn from '../utils/tokens/is-logged-in'
|
||||
|
||||
export default useAddItem as UseAddItem<typeof handler>
|
||||
|
||||
export const handler: MutationHook<ExplicitWishlistAddItemHook> = {
|
||||
export const handler: MutationHook<AddItemHook> = {
|
||||
fetchOptions: {
|
||||
url: 'wishlists',
|
||||
query: 'addWishedItem',
|
||||
@ -31,7 +32,7 @@ export const handler: MutationHook<ExplicitWishlistAddItemHook> = {
|
||||
)
|
||||
|
||||
const {
|
||||
item: { productId, variantId, wishlistToken },
|
||||
item: { variantId, wishlistToken },
|
||||
} = input
|
||||
|
||||
if (!isLoggedIn() || !wishlistToken) {
|
||||
|
@ -2,8 +2,8 @@ import { useCallback } from 'react'
|
||||
import type { MutationHook } from '@vercel/commerce/utils/types'
|
||||
import useRemoveItem from '@vercel/commerce/wishlist/use-remove-item'
|
||||
import type { UseRemoveItem } from '@vercel/commerce/wishlist/use-remove-item'
|
||||
import type { RemoveItemHook } from '@vercel/commerce/types/wishlist'
|
||||
import useWishlist from './use-wishlist'
|
||||
import type { ExplicitWishlistRemoveItemHook } from '../types'
|
||||
import isLoggedIn from '../utils/tokens/is-logged-in'
|
||||
import ensureIToken from '../utils/tokens/ensure-itoken'
|
||||
import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token'
|
||||
@ -12,7 +12,7 @@ import type { WishedItem } from '@spree/storefront-api-v2-sdk/types/interfaces/W
|
||||
|
||||
export default useRemoveItem as UseRemoveItem<typeof handler>
|
||||
|
||||
export const handler: MutationHook<ExplicitWishlistRemoveItemHook> = {
|
||||
export const handler: MutationHook<RemoveItemHook> = {
|
||||
fetchOptions: {
|
||||
url: 'wishlists',
|
||||
query: 'removeWishedItem',
|
||||
@ -45,7 +45,7 @@ export const handler: MutationHook<ExplicitWishlistRemoveItemHook> = {
|
||||
},
|
||||
useHook: ({ fetch }) => {
|
||||
const useWrappedHook: ReturnType<
|
||||
MutationHook<ExplicitWishlistRemoveItemHook>['useHook']
|
||||
MutationHook<RemoveItemHook>['useHook']
|
||||
> = () => {
|
||||
const wishlist = useWishlist()
|
||||
|
||||
|
@ -9,6 +9,7 @@ import type { Wishlist } from '@spree/storefront-api-v2-sdk/types/interfaces/Wis
|
||||
import ensureIToken from '../utils/tokens/ensure-itoken'
|
||||
import normalizeWishlist from '../utils/normalizations/normalize-wishlist'
|
||||
import isLoggedIn from '../utils/tokens/is-logged-in'
|
||||
import { ValidationError } from '@vercel/commerce/utils/errors'
|
||||
|
||||
export default useWishlist as UseWishlist<typeof handler>
|
||||
|
||||
@ -28,7 +29,9 @@ export const handler: SWRHook<GetWishlistHook> = {
|
||||
)
|
||||
|
||||
if (!isLoggedIn()) {
|
||||
return null
|
||||
throw new ValidationError({
|
||||
message: 'Not logged in',
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Optimize with includeProducts.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user