mirror of
https://github.com/vercel/commerce.git
synced 2025-05-17 15:06:59 +00:00
Cart types progress, add zod & initial schema validator
This commit is contained in:
parent
110977424d
commit
15d11aeba1
@ -49,16 +49,16 @@ export type CartTypes = {
|
||||
itemBody: CartItemBody
|
||||
}
|
||||
|
||||
export type CartHooks = Core.CartHooks<CartTypes>
|
||||
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<CartTypes>
|
||||
export type CartSchema = Core.CartSchema
|
||||
|
||||
export type CartHandlers = Core.CartHandlers<CartTypes>
|
||||
export type CartHandlers = Core.CartHandlers
|
||||
|
||||
export type GetCartHandler = CartHandlers['getCart']
|
||||
export type AddItemHandler = CartHandlers['addItem']
|
||||
|
@ -11,11 +11,14 @@ import type { WishlistSchema } from '../types/wishlist'
|
||||
import type { CheckoutSchema } from '../types/checkout'
|
||||
import type { CustomerCardSchema } from '../types/customer/card'
|
||||
import type { CustomerAddressSchema } from '../types/customer/address'
|
||||
|
||||
import { withSchemaParser } from './utils/with-schema-parser'
|
||||
|
||||
import {
|
||||
defaultOperations,
|
||||
OPERATIONS,
|
||||
AllOperations,
|
||||
APIOperations,
|
||||
defaultOperations,
|
||||
} from './operations'
|
||||
|
||||
export type APISchemas =
|
||||
@ -106,7 +109,10 @@ export function getCommerceApi<P extends APIProvider>(
|
||||
OPERATIONS.forEach((k) => {
|
||||
const op = ops[k]
|
||||
if (op) {
|
||||
commerce[k] = op({ commerce }) as AllOperations<P>[typeof k]
|
||||
commerce[k] = withSchemaParser(
|
||||
k,
|
||||
op({ commerce })
|
||||
) as AllOperations<P>[typeof k]
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -32,6 +32,15 @@ export const defaultOperations = OPERATIONS.reduce((ops, k) => {
|
||||
|
||||
export type AllowedOperations = typeof OPERATIONS[number]
|
||||
|
||||
export type OperationsData = GetProductOperation['data'] &
|
||||
GetAllProductsOperation['data'] &
|
||||
GetAllProductPathsOperation['data'] &
|
||||
GetCustomerWishlistOperation['data'] &
|
||||
GetSiteInfoOperation['data'] &
|
||||
GetPageOperation['data'] &
|
||||
GetAllPagesOperation['data'] &
|
||||
LoginOperation['data']
|
||||
|
||||
export type Operations<P extends APIProvider> = {
|
||||
login: {
|
||||
<T extends LoginOperation>(opts: {
|
||||
|
48
packages/commerce/src/api/utils/with-schema-parser.ts
Normal file
48
packages/commerce/src/api/utils/with-schema-parser.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
import { productSchema } from '../../schemas/product'
|
||||
import { CommerceError } from '../../utils/errors'
|
||||
|
||||
import type { AllowedOperations, OperationsData } from '../operations'
|
||||
|
||||
export const withSchemaParser =
|
||||
(
|
||||
operation: AllowedOperations,
|
||||
fn: (...args: any[]) => Promise<OperationsData>
|
||||
) =>
|
||||
async (...args: any[]) => {
|
||||
try {
|
||||
const result = await fn(...args)
|
||||
parse(operation, result)
|
||||
return result
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
throw new CommerceError({
|
||||
code: 'SCHEMA_VALIDATION_ERROR',
|
||||
message:
|
||||
`The ${operation} opration returned invalid data: \n` +
|
||||
error.issues
|
||||
.map((e) => `- ${e.path.join('.')}: ${e.message}`)
|
||||
.join('\n'),
|
||||
})
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const parse = (operation: AllowedOperations, data: OperationsData) => {
|
||||
switch (operation) {
|
||||
case 'getProduct':
|
||||
productSchema.parse(data.product)
|
||||
break
|
||||
|
||||
case 'getAllProducts':
|
||||
data.products?.forEach((product: any) => productSchema.parse(product))
|
||||
break
|
||||
|
||||
case 'getAllProductPaths':
|
||||
data.products?.forEach((p) => z.string().parse(p.path))
|
||||
break
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import type { AddItemHook } from '../types/cart'
|
||||
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: HookFetcherFn<AddItemHook> = mutationFetcher
|
||||
|
@ -4,9 +4,8 @@ import type { SWRHook, HookFetcherFn } from '../utils/types'
|
||||
import type { GetCartHook } from '../types/cart'
|
||||
import { Provider, useCommerce } from '..'
|
||||
|
||||
export type UseCart<
|
||||
H extends SWRHook<GetCartHook<any>> = SWRHook<GetCartHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
export type UseCart<H extends SWRHook<GetCartHook> = SWRHook<GetCartHook>> =
|
||||
ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<GetCartHook> = async ({
|
||||
options,
|
||||
|
@ -5,7 +5,7 @@ import type { RemoveItemHook } from '../types/cart'
|
||||
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 { UpdateItemHook } from '../types/cart'
|
||||
import type { Provider } from '..'
|
||||
|
||||
export type UseUpdateItem<
|
||||
H extends MutationHook<UpdateItemHook<any>> = MutationHook<UpdateItemHook>
|
||||
H extends MutationHook<UpdateItemHook> = MutationHook<UpdateItemHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<UpdateItemHook> = mutationFetcher
|
||||
|
@ -6,7 +6,7 @@ import { useHook, useMutationHook } from '../../utils/use-hook'
|
||||
import { mutationFetcher } from '../../utils/default-fetcher'
|
||||
|
||||
export type UseAddItem<
|
||||
H extends MutationHook<AddItemHook<any>> = MutationHook<AddItemHook>
|
||||
H extends MutationHook<AddItemHook> = MutationHook<AddItemHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<AddItemHook> = mutationFetcher
|
||||
|
@ -7,7 +7,7 @@ import { useHook, useSWRHook } from '../../utils/use-hook'
|
||||
import { Provider, useCommerce } from '../..'
|
||||
|
||||
export type UseAddresses<
|
||||
H extends SWRHook<GetAddressesHook<any>> = SWRHook<GetAddressesHook>
|
||||
H extends SWRHook<GetAddressesHook> = SWRHook<GetAddressesHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<GetAddressesHook> = async ({
|
||||
|
@ -6,7 +6,7 @@ import { useHook, useMutationHook } from '../../utils/use-hook'
|
||||
import { mutationFetcher } from '../../utils/default-fetcher'
|
||||
|
||||
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
|
||||
|
@ -6,7 +6,7 @@ import { useHook, useMutationHook } from '../../utils/use-hook'
|
||||
import { mutationFetcher } from '../../utils/default-fetcher'
|
||||
|
||||
export type UseUpdateItem<
|
||||
H extends MutationHook<UpdateItemHook<any>> = MutationHook<UpdateItemHook>
|
||||
H extends MutationHook<UpdateItemHook> = MutationHook<UpdateItemHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<UpdateItemHook> = mutationFetcher
|
||||
|
@ -6,7 +6,7 @@ import { useHook, useMutationHook } from '../../utils/use-hook'
|
||||
import { mutationFetcher } from '../../utils/default-fetcher'
|
||||
|
||||
export type UseAddItem<
|
||||
H extends MutationHook<AddItemHook<any>> = MutationHook<AddItemHook>
|
||||
H extends MutationHook<AddItemHook> = MutationHook<AddItemHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<AddItemHook> = mutationFetcher
|
||||
|
@ -6,9 +6,8 @@ import Cookies from 'js-cookie'
|
||||
import { useHook, useSWRHook } from '../../utils/use-hook'
|
||||
import { Provider, useCommerce } from '../..'
|
||||
|
||||
export type UseCards<
|
||||
H extends SWRHook<GetCardsHook<any>> = SWRHook<GetCardsHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
export type UseCards<H extends SWRHook<GetCardsHook> = SWRHook<GetCardsHook>> =
|
||||
ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<GetCardsHook> = async ({
|
||||
options,
|
||||
|
@ -6,7 +6,7 @@ import { useHook, useMutationHook } from '../../utils/use-hook'
|
||||
import { mutationFetcher } from '../../utils/default-fetcher'
|
||||
|
||||
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
|
||||
|
@ -6,7 +6,7 @@ import { useHook, useMutationHook } from '../../utils/use-hook'
|
||||
import { mutationFetcher } from '../../utils/default-fetcher'
|
||||
|
||||
export type UseUpdateItem<
|
||||
H extends MutationHook<UpdateItemHook<any>> = MutationHook<UpdateItemHook>
|
||||
H extends MutationHook<UpdateItemHook> = MutationHook<UpdateItemHook>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<UpdateItemHook> = mutationFetcher
|
||||
|
5
packages/commerce/src/schemas/index.ts
Normal file
5
packages/commerce/src/schemas/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import * as product from './product'
|
||||
|
||||
export default {
|
||||
product,
|
||||
}
|
48
packages/commerce/src/schemas/product.ts
Normal file
48
packages/commerce/src/schemas/product.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
export const productPriceSchema = z.object({
|
||||
value: z.number(),
|
||||
currencyCode: z.string().optional(),
|
||||
retailPrice: z.number().optional(),
|
||||
})
|
||||
|
||||
export const productOptionSchema = z.object({
|
||||
id: z.string(),
|
||||
displayName: z.string(),
|
||||
values: z.array(
|
||||
z.object({
|
||||
label: z.string(),
|
||||
hexColors: z.array(z.string()).optional(),
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
export const productImageSchema = z.object({
|
||||
url: z.string(),
|
||||
alt: z.string().optional(),
|
||||
width: z.number().optional(),
|
||||
height: z.number().optional(),
|
||||
})
|
||||
|
||||
export const productVariantSchema = z.object({
|
||||
id: z.string(),
|
||||
sku: z.string(),
|
||||
name: z.string(),
|
||||
options: z.array(productOptionSchema),
|
||||
image: productImageSchema.optional(),
|
||||
})
|
||||
|
||||
export const productSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
descriptionHtml: z.string().optional(),
|
||||
sku: z.string().optional(),
|
||||
slug: z.string(),
|
||||
path: z.string().startsWith('/'),
|
||||
images: z.array(productImageSchema),
|
||||
variants: z.array(productVariantSchema),
|
||||
price: productPriceSchema,
|
||||
options: z.array(productOptionSchema),
|
||||
vendor: z.string().optional(),
|
||||
})
|
@ -1,108 +1,135 @@
|
||||
import type { Discount, Measurement, Image } from './common'
|
||||
import type { Discount, Image } from './common'
|
||||
|
||||
export type SelectedOption = {
|
||||
// TODO: This should use the same type as the `ProductVariant` type from `product.ts`
|
||||
export interface ProductVariant {
|
||||
/**
|
||||
* The variant's id.
|
||||
*/
|
||||
id: string | number
|
||||
/**
|
||||
* The SKU (stock keeping unit) associated with the product variant.
|
||||
*/
|
||||
sku: string
|
||||
/**
|
||||
* The product variant’s name, or the product's name.
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* The product variant’s price after all discounts are applied.
|
||||
*/
|
||||
price: number
|
||||
/**
|
||||
* Product variant’s price, as quoted by the manufacturer/distributor.
|
||||
*/
|
||||
listPrice: number
|
||||
/**
|
||||
* Indicates if the variant is available for sale.
|
||||
*/
|
||||
availableForSale?: boolean
|
||||
/**
|
||||
* Whether a customer needs to provide a shipping address when placing
|
||||
* an order for the product variant.
|
||||
*/
|
||||
requiresShipping?: boolean
|
||||
/**
|
||||
* The image associated with the variant.
|
||||
*/
|
||||
image?: Image
|
||||
}
|
||||
|
||||
export interface SelectedOption {
|
||||
/**
|
||||
* The selected option's id
|
||||
*/
|
||||
id?: string
|
||||
/**
|
||||
* The product option’s name. */
|
||||
* The product option’s name.
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* The product option’s value. */
|
||||
* The product option’s value.
|
||||
*/
|
||||
value: string
|
||||
}
|
||||
|
||||
export type ProductVariant = {
|
||||
export interface LineItem {
|
||||
/**
|
||||
* The variant's id. */
|
||||
* The line item's id.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* The SKU (stock keeping unit) associated with the product variant. */
|
||||
sku: string
|
||||
/**
|
||||
* The product variant’s title, or the product's name. */
|
||||
name: string
|
||||
/**
|
||||
* Whether a customer needs to provide a shipping address when placing
|
||||
* an order for the product variant. */
|
||||
requiresShipping: boolean
|
||||
/**
|
||||
* The product variant’s price after all discounts are applied. */
|
||||
price: number
|
||||
/**
|
||||
* Product variant’s price, as quoted by the manufacturer/distributor. */
|
||||
listPrice: number
|
||||
/**
|
||||
* Image associated with the product variant. Falls back to the product image
|
||||
* if no image is available. */
|
||||
image?: Image
|
||||
/**
|
||||
* Indicates whether this product variant is in stock. */
|
||||
isInStock?: boolean
|
||||
/**
|
||||
* Indicates if the product variant is available for sale. */
|
||||
availableForSale?: boolean
|
||||
/**
|
||||
* The variant's weight. If a weight was not explicitly specified on the */
|
||||
/**
|
||||
* variant this will be the product's weight. */
|
||||
weight?: Measurement
|
||||
/**
|
||||
* The variant's height. If a height was not explicitly specified on the
|
||||
* variant, this will be the product's height. */
|
||||
height?: Measurement
|
||||
/**
|
||||
* The variant's width. If a width was not explicitly specified on the
|
||||
|
||||
* variant, this will be the product's width. */
|
||||
width?: Measurement
|
||||
/**
|
||||
* The variant's depth. If a depth was not explicitly specified on the
|
||||
* variant, this will be the product's depth. */
|
||||
depth?: Measurement
|
||||
}
|
||||
|
||||
export type LineItem = {
|
||||
id: string
|
||||
* The product variant’s id.
|
||||
*/
|
||||
variantId: string
|
||||
/**
|
||||
* The product's id.
|
||||
*/
|
||||
productId: string
|
||||
/**
|
||||
* The name of the line item.
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* List of discounts applied to the line item.
|
||||
*/
|
||||
quantity: number
|
||||
/**
|
||||
* List of discounts applied to the line item.
|
||||
*/
|
||||
discounts: Discount[]
|
||||
/**
|
||||
* A human-friendly unique string automatically generated from the product’s name. */
|
||||
* A human-friendly unique string automatically generated from the product’s name.
|
||||
*/
|
||||
path: string
|
||||
/**
|
||||
* The product variant.
|
||||
*/
|
||||
variant: ProductVariant
|
||||
/**
|
||||
* List of selected options.
|
||||
*/
|
||||
options?: SelectedOption[]
|
||||
}
|
||||
|
||||
// Shopping cart, a.k.a Checkout
|
||||
|
||||
export type Cart = {
|
||||
/**
|
||||
* The cart's id. */
|
||||
* Shopping cart, a.k.a Checkout
|
||||
*/
|
||||
export interface Cart {
|
||||
/**
|
||||
* The cart's id.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* ID of the customer to which the cart belongs. */
|
||||
* ID of the customer to which the cart belongs.
|
||||
*/
|
||||
customerId?: string
|
||||
/**
|
||||
* The email assigned to this cart. */
|
||||
* The URL of the cart.
|
||||
*/
|
||||
url?: string
|
||||
/**
|
||||
* The email assigned to this cart.
|
||||
*/
|
||||
email?: string
|
||||
/**
|
||||
* The date and time when the cart was created. */
|
||||
* The date and time when the cart was created.
|
||||
*/
|
||||
createdAt: string
|
||||
/**
|
||||
* The currency used for this cart */
|
||||
currency: { code: string }
|
||||
/**
|
||||
* Specifies if taxes are included in the line items. */
|
||||
* Specifies if taxes are included in the line items.
|
||||
*/
|
||||
taxesIncluded: boolean
|
||||
/**
|
||||
* List of cart line items.
|
||||
*/
|
||||
lineItems: LineItem[]
|
||||
/**
|
||||
* The sum of all the prices of all the items in the cart. */
|
||||
/**
|
||||
* Duties, taxes, shipping and discounts excluded. */
|
||||
* The sum of all the pricexs of all the items in the cart.
|
||||
* Duties, taxes, shipping and discounts excluded.
|
||||
*/
|
||||
lineItemsSubtotalPrice: number
|
||||
/**
|
||||
* Price of the cart before duties, shipping and taxes.*/
|
||||
@ -120,93 +147,119 @@ export type Cart = {
|
||||
/**
|
||||
* Base cart item body used for cart mutations
|
||||
*/
|
||||
export type CartItemBody = {
|
||||
export interface CartItemBody {
|
||||
/**
|
||||
* The product variant's id.
|
||||
*/
|
||||
variantId: string
|
||||
/**
|
||||
* The product's id.
|
||||
*/
|
||||
productId?: string
|
||||
/**
|
||||
* The quantity of the product variant.
|
||||
*/
|
||||
quantity?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks schema
|
||||
* Hooks for add, update & remove items from the cart.
|
||||
*/
|
||||
|
||||
export type CartTypes = {
|
||||
cart?: Cart
|
||||
item: LineItem
|
||||
itemBody: CartItemBody
|
||||
export type CartHooks = {
|
||||
getCart: GetCartHook
|
||||
addItem: AddItemHook
|
||||
updateItem: UpdateItemHook
|
||||
removeItem: RemoveItemHook
|
||||
}
|
||||
|
||||
export type CartHooks<T extends CartTypes = CartTypes> = {
|
||||
getCart: GetCartHook<T>
|
||||
addItem: AddItemHook<T>
|
||||
updateItem: UpdateItemHook<T>
|
||||
removeItem: RemoveItemHook<T>
|
||||
}
|
||||
|
||||
export type GetCartHook<T extends CartTypes = CartTypes> = {
|
||||
data: T['cart'] | null
|
||||
/**
|
||||
* Hook for getting the cart.
|
||||
*/
|
||||
export interface GetCartHook {
|
||||
data: Cart | null
|
||||
input: {}
|
||||
fetcherInput: { cartId?: string }
|
||||
swrState: { isEmpty: boolean }
|
||||
}
|
||||
|
||||
export type AddItemHook<T extends CartTypes = CartTypes> = {
|
||||
data: T['cart']
|
||||
input?: T['itemBody']
|
||||
fetcherInput: T['itemBody']
|
||||
body: { item: T['itemBody'] }
|
||||
actionInput: T['itemBody']
|
||||
/**
|
||||
* Hook for adding an item to the cart.
|
||||
*/
|
||||
export interface AddItemHook {
|
||||
data: Cart
|
||||
input?: CartItemBody
|
||||
fetcherInput: CartItemBody
|
||||
body: { item: CartItemBody }
|
||||
actionInput: CartItemBody
|
||||
}
|
||||
|
||||
export type UpdateItemHook<T extends CartTypes = CartTypes> = {
|
||||
data: T['cart'] | null
|
||||
input: { item?: T['item']; wait?: number }
|
||||
fetcherInput: { itemId: string; item: T['itemBody'] }
|
||||
body: { itemId: string; item: T['itemBody'] }
|
||||
actionInput: T['itemBody'] & { id: string }
|
||||
/**
|
||||
* Hook for updating an item in the cart.
|
||||
*/
|
||||
export interface UpdateItemHook {
|
||||
data: Cart | null | undefined
|
||||
input: { item?: LineItem; wait?: number }
|
||||
fetcherInput: { itemId: string; item: CartItemBody }
|
||||
body: { itemId: string; item: CartItemBody }
|
||||
actionInput: CartItemBody & { id: string }
|
||||
}
|
||||
|
||||
export type RemoveItemHook<T extends CartTypes = CartTypes> = {
|
||||
data: T['cart'] | null
|
||||
input: { item?: T['item'] }
|
||||
/**
|
||||
* Hook for removing an item from the cart.
|
||||
*/
|
||||
export interface RemoveItemHook {
|
||||
data: Cart | null | undefined
|
||||
input: { item?: LineItem }
|
||||
fetcherInput: { itemId: string }
|
||||
body: { itemId: string }
|
||||
actionInput: { id: string }
|
||||
}
|
||||
|
||||
/**
|
||||
* API Schema
|
||||
* Cart API Schema.
|
||||
*/
|
||||
|
||||
export type CartSchema<T extends CartTypes = CartTypes> = {
|
||||
export type CartSchema = {
|
||||
endpoint: {
|
||||
options: {}
|
||||
handlers: CartHandlers<T>
|
||||
handlers: CartHandlers
|
||||
}
|
||||
}
|
||||
|
||||
export type CartHandlers<T extends CartTypes = CartTypes> = {
|
||||
getCart: GetCartHandler<T>
|
||||
addItem: AddItemHandler<T>
|
||||
updateItem: UpdateItemHandler<T>
|
||||
removeItem: RemoveItemHandler<T>
|
||||
/**
|
||||
* API Handlers for adding, updating & removing items from the cart.
|
||||
*/
|
||||
export type CartHandlers = {
|
||||
getCart: GetCartHandler
|
||||
addItem: AddItemHandler
|
||||
updateItem: UpdateItemHandler
|
||||
removeItem: RemoveItemHandler
|
||||
}
|
||||
|
||||
export type GetCartHandler<T extends CartTypes = CartTypes> = GetCartHook<T> & {
|
||||
/**
|
||||
* API Handler for getting the cart.
|
||||
*/
|
||||
export type GetCartHandler = GetCartHook & {
|
||||
body: { cartId?: string }
|
||||
}
|
||||
|
||||
export type AddItemHandler<T extends CartTypes = CartTypes> = AddItemHook<T> & {
|
||||
/**
|
||||
* API Handler for adding an item to the cart.
|
||||
*/
|
||||
export type AddItemHandler = AddItemHook & {
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type UpdateItemHandler<T extends CartTypes = CartTypes> =
|
||||
UpdateItemHook<T> & {
|
||||
data: T['cart']
|
||||
/**
|
||||
* API Handler for updating an item in the cart.
|
||||
*/
|
||||
export type UpdateItemHandler = UpdateItemHook & {
|
||||
data: Cart
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type RemoveItemHandler<T extends CartTypes = CartTypes> =
|
||||
RemoveItemHook<T> & {
|
||||
/**
|
||||
* API Handler for removing an item from the cart.
|
||||
*/
|
||||
export type RemoveItemHandler = RemoveItemHook & {
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
@ -1,16 +1,36 @@
|
||||
export type Discount = {
|
||||
// The value of the discount, can be an amount or percentage
|
||||
export interface Discount {
|
||||
/**
|
||||
* The value of the discount, can be an amount or percentage.
|
||||
*/
|
||||
value: number
|
||||
}
|
||||
|
||||
export type Measurement = {
|
||||
export interface Measurement {
|
||||
/**
|
||||
* The measurement's value.
|
||||
*/
|
||||
value: number
|
||||
/**
|
||||
* The measurement's unit.
|
||||
*/
|
||||
unit: 'KILOGRAMS' | 'GRAMS' | 'POUNDS' | 'OUNCES'
|
||||
}
|
||||
|
||||
export type Image = {
|
||||
export interface Image {
|
||||
/**
|
||||
* The URL of the image.
|
||||
*/
|
||||
url: string
|
||||
altText?: string
|
||||
/**
|
||||
* A word or phrase that describes the content of an image.
|
||||
*/
|
||||
alt?: string
|
||||
/**
|
||||
* The image's width.
|
||||
*/
|
||||
width?: number
|
||||
/**
|
||||
* The image's height.
|
||||
*/
|
||||
height?: number
|
||||
}
|
||||
|
@ -15,97 +15,71 @@ export interface AddressFields {
|
||||
country: string
|
||||
}
|
||||
|
||||
export type CustomerAddressTypes = {
|
||||
address?: Address
|
||||
fields: AddressFields
|
||||
}
|
||||
|
||||
export type GetAddressesHook<
|
||||
T extends CustomerAddressTypes = CustomerAddressTypes
|
||||
> = {
|
||||
data: T['address'][] | null
|
||||
export type GetAddressesHook = {
|
||||
data: Address[] | null
|
||||
input: {}
|
||||
fetcherInput: { cartId?: string }
|
||||
swrState: { isEmpty: boolean }
|
||||
}
|
||||
|
||||
export type AddItemHook<T extends CustomerAddressTypes = CustomerAddressTypes> =
|
||||
{
|
||||
data: T['address']
|
||||
input?: T['fields']
|
||||
fetcherInput: T['fields']
|
||||
body: { item: T['fields'] }
|
||||
actionInput: T['fields']
|
||||
export type AddItemHook = {
|
||||
data: Address
|
||||
input?: AddressFields
|
||||
fetcherInput: AddressFields
|
||||
body: { item: AddressFields }
|
||||
actionInput: AddressFields
|
||||
}
|
||||
|
||||
export type UpdateItemHook<
|
||||
T extends CustomerAddressTypes = CustomerAddressTypes
|
||||
> = {
|
||||
data: T['address'] | null
|
||||
input: { item?: T['fields']; wait?: number }
|
||||
fetcherInput: { itemId: string; item: T['fields'] }
|
||||
body: { itemId: string; item: T['fields'] }
|
||||
actionInput: T['fields'] & { id: string }
|
||||
export type UpdateItemHook = {
|
||||
data: Address | null
|
||||
input: { item?: AddressFields; wait?: number }
|
||||
fetcherInput: { itemId: string; item: AddressFields }
|
||||
body: { itemId: string; item: AddressFields }
|
||||
actionInput: AddressFields & { id: string }
|
||||
}
|
||||
|
||||
export type RemoveItemHook<
|
||||
T extends CustomerAddressTypes = CustomerAddressTypes
|
||||
> = {
|
||||
data: T['address'] | null
|
||||
input: { item?: T['address'] }
|
||||
export type RemoveItemHook = {
|
||||
data: Address | null
|
||||
input: { item?: Address }
|
||||
fetcherInput: { itemId: string }
|
||||
body: { itemId: string }
|
||||
actionInput: { id: string }
|
||||
}
|
||||
|
||||
export type CustomerAddressHooks<
|
||||
T extends CustomerAddressTypes = CustomerAddressTypes
|
||||
> = {
|
||||
getAddresses: GetAddressesHook<T>
|
||||
addItem: AddItemHook<T>
|
||||
updateItem: UpdateItemHook<T>
|
||||
removeItem: RemoveItemHook<T>
|
||||
export type CustomerAddressHooks = {
|
||||
getAddresses: GetAddressesHook
|
||||
addItem: AddItemHook
|
||||
updateItem: UpdateItemHook
|
||||
removeItem: RemoveItemHook
|
||||
}
|
||||
|
||||
export type AddressHandler<
|
||||
T extends CustomerAddressTypes = CustomerAddressTypes
|
||||
> = GetAddressesHook<T> & {
|
||||
export type AddressHandler = GetAddressesHook & {
|
||||
body: { cartId?: string }
|
||||
}
|
||||
|
||||
export type AddItemHandler<
|
||||
T extends CustomerAddressTypes = CustomerAddressTypes
|
||||
> = AddItemHook<T> & {
|
||||
export type AddItemHandler = AddItemHook & {
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type UpdateItemHandler<
|
||||
T extends CustomerAddressTypes = CustomerAddressTypes
|
||||
> = UpdateItemHook<T> & {
|
||||
data: T['address']
|
||||
export type UpdateItemHandler = UpdateItemHook & {
|
||||
data: Address
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type RemoveItemHandler<
|
||||
T extends CustomerAddressTypes = CustomerAddressTypes
|
||||
> = RemoveItemHook<T> & {
|
||||
export type RemoveItemHandler = RemoveItemHook & {
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type CustomerAddressHandlers<
|
||||
T extends CustomerAddressTypes = CustomerAddressTypes
|
||||
> = {
|
||||
getAddresses: GetAddressesHook<T>
|
||||
addItem: AddItemHandler<T>
|
||||
updateItem: UpdateItemHandler<T>
|
||||
removeItem: RemoveItemHandler<T>
|
||||
export type CustomerAddressHandlers = {
|
||||
getAddresses: GetAddressesHook
|
||||
addItem: AddItemHandler
|
||||
updateItem: UpdateItemHandler
|
||||
removeItem: RemoveItemHandler
|
||||
}
|
||||
|
||||
export type CustomerAddressSchema<
|
||||
T extends CustomerAddressTypes = CustomerAddressTypes
|
||||
> = {
|
||||
export type CustomerAddressSchema = {
|
||||
endpoint: {
|
||||
options: {}
|
||||
handlers: CustomerAddressHandlers<T>
|
||||
handlers: CustomerAddressHandlers
|
||||
}
|
||||
}
|
||||
|
@ -1,99 +1,134 @@
|
||||
export interface ProductImage {
|
||||
/**
|
||||
* The URL of the product image.
|
||||
*/
|
||||
url: string
|
||||
/**
|
||||
* A word or phrase that describes the content of an image.
|
||||
*/
|
||||
alt?: string
|
||||
}
|
||||
import { Image } from './common'
|
||||
|
||||
export interface ProductPrice {
|
||||
/**
|
||||
* Decimal price amount. */
|
||||
* The price after all discounts are applied.
|
||||
*/
|
||||
value: number
|
||||
/**
|
||||
* Currency of the product price. */
|
||||
* Currency code of the product price.
|
||||
*/
|
||||
currencyCode?: 'USD' | 'EUR' | 'ARS' | 'GBP' | string
|
||||
/**
|
||||
* The retail price of the product. This can be used to mark a product as on sale, when `retailPrice` is higher than the price a.k.a `value`. */
|
||||
* The retail price of the product. This can be used to mark a product as on sale, when `retailPrice` is higher than the price a.k.a `value`.
|
||||
*/
|
||||
retailPrice?: number
|
||||
|
||||
extendedSalePrice?: number
|
||||
extendedListPrice?: number
|
||||
}
|
||||
|
||||
export interface ProductOption {
|
||||
__typename?: 'MultipleChoiceOption'
|
||||
/**
|
||||
* The option's id. */
|
||||
* The option's id.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* The option's name. */
|
||||
* The option's name.
|
||||
*/
|
||||
displayName: string
|
||||
/**
|
||||
* List of option values. */
|
||||
* List of option values.
|
||||
*/
|
||||
values: ProductOptionValues[]
|
||||
}
|
||||
|
||||
export interface ProductOptionValues {
|
||||
/**
|
||||
* A string that uniquely identifies the option value. */
|
||||
* A string that uniquely identifies the option value.
|
||||
*/
|
||||
label: string
|
||||
/**
|
||||
* List of hex colors used to display the actual colors in the swatches instead of the name. */
|
||||
* List of hex colors used to display the actual colors in the swatches instead of the name.
|
||||
*/
|
||||
hexColors?: string[]
|
||||
}
|
||||
|
||||
export interface ProductVariant {
|
||||
/**
|
||||
* The variant's id. */
|
||||
* The variant's id.
|
||||
*/
|
||||
id: string | number
|
||||
/**
|
||||
* List of product options. */
|
||||
* The SKU (stock keeping unit) associated with the product variant.
|
||||
*/
|
||||
sku: string
|
||||
/**
|
||||
* The product variant’s name, or the product's name.
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* List of product options.
|
||||
*/
|
||||
options: ProductOption[]
|
||||
/**
|
||||
* Indicates if the variant is available for sale. */
|
||||
* The product variant’s price after all discounts are applied.
|
||||
*/
|
||||
price: ProductPrice
|
||||
/**
|
||||
* Product variant’s price, as quoted by the manufacturer/distributor.
|
||||
*/
|
||||
listPrice: ProductPrice
|
||||
/**
|
||||
* Indicates if the variant is available for sale.
|
||||
*/
|
||||
availableForSale?: boolean
|
||||
/**
|
||||
* Whether a customer needs to provide a shipping address when placing an order for the product variant.
|
||||
*/
|
||||
requiresShipping?: boolean
|
||||
/**
|
||||
* The image associated with the variant.
|
||||
*/
|
||||
image?: Image
|
||||
}
|
||||
|
||||
export interface Product {
|
||||
/**
|
||||
* The product's id. */
|
||||
* The product's id.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* The product's name. */
|
||||
* The product's name.
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* Stripped description of the product, single line. */
|
||||
* Stripped description of the product, single line.
|
||||
*/
|
||||
description: string
|
||||
/**
|
||||
* The description of the product, complete with HTML formatting. */
|
||||
* The description of the product, complete with HTML formatting.
|
||||
*/
|
||||
descriptionHtml?: string
|
||||
/**
|
||||
* The SKU (stock keeping unit) associated with the product.
|
||||
*/
|
||||
sku?: string
|
||||
/**
|
||||
* A human-friendly unique string for the product, automatically generated from its title. */
|
||||
* A human-friendly unique string for the product, automatically generated from its title.
|
||||
*/
|
||||
slug?: string
|
||||
/**
|
||||
* A human-friendly string for the product, containing U. */
|
||||
* A human-friendly string for the product, containing U.
|
||||
*/
|
||||
path?: string
|
||||
/**
|
||||
* List of images associated with the product. */
|
||||
images: ProductImage[]
|
||||
* List of images associated with the product.
|
||||
*/
|
||||
images: Image[]
|
||||
/**
|
||||
* List of the product’s variants. */
|
||||
* List of the product’s variants.
|
||||
*/
|
||||
variants: ProductVariant[]
|
||||
/**
|
||||
* The product's base price. Could be the minimum value, or default variant price. */
|
||||
* The product's base price. Could be the minimum value, or default variant price.
|
||||
*/
|
||||
price: ProductPrice
|
||||
/**
|
||||
* The product's price. */
|
||||
* The product's price.
|
||||
*/
|
||||
options: ProductOption[]
|
||||
/**
|
||||
* The product’s vendor name. */
|
||||
* The product’s vendor name.
|
||||
*/
|
||||
vendor?: string
|
||||
}
|
||||
|
||||
@ -123,19 +158,22 @@ export interface SearchProductsBody {
|
||||
export interface SearchProductsHook {
|
||||
data: {
|
||||
/**
|
||||
* List of products matching the query. */
|
||||
* List of products matching the query.
|
||||
*/
|
||||
products: Product[]
|
||||
/**
|
||||
* Indicates if there are any products matching the query. */
|
||||
* Indicates if there are any products matching the query.
|
||||
*/
|
||||
found: boolean
|
||||
}
|
||||
body: SearchProductsBody
|
||||
/**
|
||||
* Indicates if the request is loading. */
|
||||
input: SearchProductsBody
|
||||
fetcherInput: SearchProductsBody
|
||||
}
|
||||
|
||||
/**
|
||||
* Product API schema
|
||||
*/
|
||||
export interface ProductsSchema {
|
||||
endpoint: {
|
||||
options: {}
|
||||
@ -161,7 +199,8 @@ export interface GetAllProductsOperation {
|
||||
|
||||
export interface GetProductOperation {
|
||||
/**
|
||||
* Returned data from the operation. */
|
||||
* Returned data from the operation.
|
||||
*/
|
||||
data: { product?: Product }
|
||||
/**
|
||||
* The variables to pass to the operation.*/
|
||||
|
@ -13,18 +13,16 @@ export type Cart = Core.Cart & {
|
||||
url?: string
|
||||
}
|
||||
|
||||
export type CartTypes = Core.CartTypes
|
||||
|
||||
export type CartHooks = Core.CartHooks<CartTypes>
|
||||
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<CartTypes>
|
||||
export type CartSchema = Core.CartSchema
|
||||
|
||||
export type CartHandlers = Core.CartHandlers<CartTypes>
|
||||
export type CartHandlers = Core.CartHandlers
|
||||
|
||||
export type GetCartHandler = CartHandlers['getCart']
|
||||
export type AddItemHandler = CartHandlers['addItem']
|
||||
|
@ -2,7 +2,7 @@ import { useCallback } from 'react'
|
||||
import type { MutationHook } from '@vercel/commerce/utils/types'
|
||||
import { CommerceError } from '@vercel/commerce/utils/errors'
|
||||
import useAddItem, { UseAddItem } from '@vercel/commerce/cart/use-add-item'
|
||||
import type { AddItemHook } from '../types/cart'
|
||||
import type { AddItemHook } from '@vercel/commerce/types/cart'
|
||||
import useCart from './use-cart'
|
||||
|
||||
import {
|
||||
|
@ -1,32 +1 @@
|
||||
import * as Core from '@vercel/commerce/types/cart'
|
||||
|
||||
export * from '@vercel/commerce/types/cart'
|
||||
|
||||
export type ShopifyCart = {}
|
||||
|
||||
/**
|
||||
* 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']
|
||||
|
@ -93,7 +93,7 @@ const CartItem = ({
|
||||
width={150}
|
||||
height={150}
|
||||
src={item.variant.image?.url || placeholderImg}
|
||||
alt={item.variant.image?.altText || "Product Image"}
|
||||
alt={item.variant.image?.alt || 'Product Image'}
|
||||
unoptimized
|
||||
/>
|
||||
</a>
|
||||
|
@ -45,7 +45,8 @@
|
||||
"react-merge-refs": "^2.0.1",
|
||||
"react-use-measure": "^2.1.1",
|
||||
"tabbable": "^5.2.1",
|
||||
"tailwindcss": "^3.0.13"
|
||||
"tailwindcss": "^3.0.13",
|
||||
"zod": "^3.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "^12.0.8",
|
||||
|
Loading…
x
Reference in New Issue
Block a user