mirror of
https://github.com/vercel/commerce.git
synced 2025-05-17 15:06:59 +00:00
Merge branch 'main' into update-image
This commit is contained in:
commit
2805323513
16
README.md
16
README.md
@ -20,11 +20,11 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/)
|
||||
|
||||
## Run minimal version locally
|
||||
|
||||
> To run a minimal version of Next.js Commerce you can start with the default local provider `@vercel/commerce-local` that has disabled all features (cart, auth) and use static files for the backend
|
||||
> To run a minimal version of Next.js Commerce you can start with the default local provider `@vercel/commerce-local` that has all features disabled (cart, auth) and uses static files for the backend
|
||||
|
||||
```bash
|
||||
pnpm install & pnpm build # run this commands in root folder of the mono repo
|
||||
pnpm dev # run this commands in the site folder
|
||||
pnpm install & pnpm build # run these commands in the root folder of the mono repo
|
||||
pnpm dev # run this command in the site folder
|
||||
```
|
||||
|
||||
> If you encounter any problems while installing and running for the first time, please see the Troubleshoot section
|
||||
@ -47,10 +47,10 @@ Next.js Commerce integrates out-of-the-box with BigCommerce, Shopify, Swell, Sal
|
||||
|
||||
## Considerations
|
||||
|
||||
- `packages/commerce` contains all types, helpers and functions to be used as base to build a new **provider**.
|
||||
- `packages/commerce` contains all types, helpers and functions to be used as a base to build a new **provider**.
|
||||
- **Providers** live under `packages`'s root folder and they will extend Next.js Commerce types and functionality (`packages/commerce`).
|
||||
- We have a **Features API** to ensure feature parity between the UI and the Provider. The UI should update accordingly and no extra code should be bundled. All extra configuration for features will live under `features` in `commerce.config.json` and if needed it can also be accessed programatically.
|
||||
- Each **provider** should add its corresponding `next.config.js` and `commerce.config.json` adding specific data related to the provider. For example in case of BigCommerce, the images CDN and additional API routes.
|
||||
- We have a **Features API** to ensure feature parity between the UI and the Provider. The UI should update accordingly and no extra code should be bundled. All extra configuration for features will live under `features` in `commerce.config.json` and if needed it can also be accessed programmatically.
|
||||
- Each **provider** should add its corresponding `next.config.js` and `commerce.config.json` adding specific data related to the provider. For example in the case of BigCommerce, the images CDN and additional API routes.
|
||||
|
||||
## Configuration
|
||||
|
||||
@ -73,7 +73,7 @@ Every provider defines the features that it supports under `packages/{provider}/
|
||||
#### Features Available
|
||||
|
||||
The following features can be enabled or disabled. This means that the UI will remove all code related to the feature.
|
||||
For example: Turning `cart` off will disable Cart capabilities.
|
||||
For example: turning `cart` off will disable Cart capabilities.
|
||||
|
||||
- cart
|
||||
- search
|
||||
@ -83,7 +83,7 @@ For example: Turning `cart` off will disable Cart capabilities.
|
||||
|
||||
#### How to turn Features on and off
|
||||
|
||||
> NOTE: The selected provider should support the feature that you are toggling. (This means that you can't turn wishlist on if the provider doesn't support this functionality out the box)
|
||||
> NOTE: The selected provider should support the feature that you are toggling. (This means that you can't turn wishlist on if the provider doesn't support this functionality out of the box)
|
||||
|
||||
- Open `site/commerce.config.json`
|
||||
- You'll see a config file like this:
|
||||
|
@ -42,7 +42,9 @@ export class CommerceNetworkError extends Error {
|
||||
}
|
||||
|
||||
export const normalizeZodIssues = (issues: ZodError['issues']) =>
|
||||
issues.map(({ path, message }) => `${message} at "${path.join('.')}" field`)
|
||||
issues.map(({ path, message }) =>
|
||||
path.length ? `${message} at "${path.join('.')}" field` : message
|
||||
)
|
||||
|
||||
export const getOperationError = (operation: string, error: unknown) => {
|
||||
if (error instanceof ZodError) {
|
||||
|
@ -21,7 +21,7 @@ export const withOperationCallback =
|
||||
const parse = ({ name, data }: Operation) => {
|
||||
switch (name) {
|
||||
case 'getProduct':
|
||||
productSchema.nullable().parse(data.product)
|
||||
productSchema.optional().parse(data.product)
|
||||
break
|
||||
case 'getAllProducts':
|
||||
z.array(productSchema).parse(data.products)
|
||||
|
@ -27,7 +27,7 @@ export default function getSiteInfoOperation({
|
||||
const { sdkFetch } = commerce.getConfig(config)
|
||||
const { data: categories } = await sdkFetch('categories', 'list')
|
||||
|
||||
const formattedCategories = categories.map(normalizeCategory)
|
||||
const formattedCategories = categories?.map(normalizeCategory) ?? []
|
||||
|
||||
return {
|
||||
categories: formattedCategories,
|
||||
|
@ -22,17 +22,17 @@ export const handler: MutationHook<AddItemHook> = {
|
||||
variables.push(item.variantId)
|
||||
}
|
||||
|
||||
const { cart } = await fetch<{ cart: CommercejsCart }>({
|
||||
const cart = await fetch<CommercejsCart>({
|
||||
query: options.query,
|
||||
method: options.method,
|
||||
variables,
|
||||
})
|
||||
|
||||
return normalizeCart(cart)
|
||||
},
|
||||
useHook: ({ fetch }) =>
|
||||
function useHook() {
|
||||
const { mutate } = useCart()
|
||||
|
||||
return useCallback(
|
||||
async function addItem(input) {
|
||||
const cart = await fetch({ input })
|
||||
|
@ -16,7 +16,7 @@ export const handler: MutationHook<RemoveItemHook> = {
|
||||
method: 'remove',
|
||||
},
|
||||
async fetcher({ input, options, fetch }) {
|
||||
const { cart } = await fetch<{ cart: CommercejsCart }>({
|
||||
const cart = await fetch<CommercejsCart>({
|
||||
query: options.query,
|
||||
method: options.method,
|
||||
variables: input.itemId,
|
||||
|
@ -30,7 +30,7 @@ export const handler = {
|
||||
},
|
||||
async fetcher({ input, options, fetch }: HookFetcherContext<UpdateItemHook>) {
|
||||
const variables = [input.itemId, { quantity: input.item.quantity }]
|
||||
const { cart } = await fetch<{ cart: CommercejsCart }>({
|
||||
const cart = await fetch<CommercejsCart>({
|
||||
query: options.query,
|
||||
method: options.method,
|
||||
variables,
|
||||
@ -57,7 +57,7 @@ export const handler = {
|
||||
const variantId = input.productId ?? item?.variantId
|
||||
const quantity = input?.quantity ?? item?.quantity
|
||||
|
||||
if (!itemId || !productId || !variantId) {
|
||||
if (!itemId || !productId) {
|
||||
throw new ValidationError({
|
||||
message: 'Invalid input for updating cart item',
|
||||
})
|
||||
@ -69,7 +69,7 @@ export const handler = {
|
||||
item: {
|
||||
quantity,
|
||||
productId,
|
||||
variantId,
|
||||
variantId: variantId ?? '',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -44,14 +44,16 @@ const normalizeLineItem = (
|
||||
}
|
||||
}
|
||||
|
||||
export const normalizeCart = (commercejsCart: CommercejsCart): Cart => {
|
||||
export const normalizeCart = (
|
||||
commercejsCart: CommercejsCart | { cart: CommercejsCart }
|
||||
): Cart => {
|
||||
const {
|
||||
id,
|
||||
created,
|
||||
subtotal: { raw: rawPrice },
|
||||
currency,
|
||||
line_items,
|
||||
} = commercejsCart
|
||||
} = 'cart' in commercejsCart ? commercejsCart.cart : commercejsCart
|
||||
|
||||
return {
|
||||
id,
|
||||
|
@ -54,6 +54,7 @@ export function normalizeProduct(
|
||||
): Product {
|
||||
const { id, name, description, permalink, assets, price, variant_groups } =
|
||||
commercejsProduct
|
||||
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
@ -61,15 +62,19 @@ export function normalizeProduct(
|
||||
descriptionHtml: description,
|
||||
slug: permalink,
|
||||
path: `/${permalink}`,
|
||||
images: assets.map(({ url, description, filename }) => ({
|
||||
images:
|
||||
assets?.map(({ url, description, filename }) => ({
|
||||
url,
|
||||
alt: description || filename,
|
||||
})),
|
||||
})) || [],
|
||||
price: {
|
||||
value: price.raw,
|
||||
currencyCode: 'USD',
|
||||
},
|
||||
variants: normalizeVariants(commercejsProductVariants, variant_groups),
|
||||
options: getOptionsFromVariantGroups(variant_groups),
|
||||
variants: normalizeVariants(
|
||||
commercejsProductVariants,
|
||||
variant_groups || []
|
||||
),
|
||||
options: variant_groups ? getOptionsFromVariantGroups(variant_groups) : [],
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import type { GraphQLFetcher } from '@vercel/commerce/api'
|
||||
|
||||
import { API_URL } from '../../const'
|
||||
import { getError } from '../../utils/handle-fetch-response'
|
||||
import { getCommerceApi } from '..'
|
||||
import { getToken } from '../../utils/index'
|
||||
|
||||
const fetchGraphqlApi: GraphQLFetcher = async (
|
||||
@ -10,7 +9,6 @@ const fetchGraphqlApi: GraphQLFetcher = async (
|
||||
{ variables } = {},
|
||||
headers?: HeadersInit
|
||||
) => {
|
||||
const config = getCommerceApi().getConfig()
|
||||
const token = getToken()
|
||||
|
||||
const res = await fetch(API_URL!, {
|
||||
@ -28,10 +26,17 @@ const fetchGraphqlApi: GraphQLFetcher = async (
|
||||
}),
|
||||
})
|
||||
|
||||
const { data, errors, status } = await res.json()
|
||||
const { data, errors, message, type, status } = await res.json()
|
||||
|
||||
if (errors) {
|
||||
throw getError(errors, status)
|
||||
if (errors || res.status >= 400) {
|
||||
throw getError(
|
||||
errors || [
|
||||
{
|
||||
message: `${type ? `${type}, ` : ''}${message}`,
|
||||
},
|
||||
],
|
||||
status || res.status
|
||||
)
|
||||
}
|
||||
|
||||
return { data, res }
|
||||
|
@ -35,7 +35,9 @@ export async function getStaticProps({
|
||||
const { products: relatedProducts } = await allProductsPromise
|
||||
|
||||
if (!product) {
|
||||
throw new Error(`Product with slug '${params!.slug}' not found`)
|
||||
return {
|
||||
notFound: true,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
Loading…
x
Reference in New Issue
Block a user