Cart types progress, add zod & initial schema validator

This commit is contained in:
Catalin Pinte 2022-09-07 11:09:10 +03:00
parent 110977424d
commit 15d11aeba1
27 changed files with 452 additions and 284 deletions

View File

@ -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']

View File

@ -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]
}
})

View File

@ -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: {

View 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
}
}

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ({

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
import * as product from './product'
export default {
product,
}

View 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(),
})

View File

@ -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 variants name, or the product's name.
*/
name: string
/**
* The product variants price after all discounts are applied.
*/
price: number
/**
* Product variants 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 options name. */
* The product options name.
*/
name: string
/**
* The product options value. */
* The product options 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 variants 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 variants price after all discounts are applied. */
price: number
/**
* Product variants 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 variants 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 products name. */
* A human-friendly unique string automatically generated from the products name.
*/
path: string
/**
* The product variant.
*/
variant: ProductVariant
/**
* List of selected options.
*/
options?: SelectedOption[]
}
// Shopping cart, a.k.a Checkout
export type Cart = {
/**
* Shopping cart, a.k.a Checkout
*/
export interface Cart {
/**
* The cart's id. */
* 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 }
}
}

View File

@ -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
}

View File

@ -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 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 AddItemHook = {
data: Address
input?: AddressFields
fetcherInput: AddressFields
body: { item: AddressFields }
actionInput: AddressFields
}
export type RemoveItemHook<
T extends CustomerAddressTypes = CustomerAddressTypes
> = {
data: T['address'] | null
input: { item?: T['address'] }
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 = {
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
}
}

View File

@ -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 variants name, or the product's name.
*/
name: string
/**
* List of product options.
*/
options: ProductOption[]
/**
* Indicates if the variant is available for sale. */
* The product variants price after all discounts are applied.
*/
price: ProductPrice
/**
* Product variants 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 products variants. */
* List of the products 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 products vendor name. */
* The products 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.*/

View File

@ -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']

View File

@ -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 {

View File

@ -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']

View File

@ -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>

View File

@ -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",