Updates & fixes

This commit is contained in:
Catalin Pinte 2022-11-28 12:09:01 +02:00
parent 6699f2fed4
commit 36e68b461f
9 changed files with 1740 additions and 1551 deletions

View File

@ -5,10 +5,12 @@ import type {
import type {
GetCustomerWishlistOperation,
Wishlist,
WishlistItem,
} from '@vercel/commerce/types/wishlist'
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
import type { RecursivePartial } from '../utils/types'
import { BigcommerceConfig, Provider } from '..'
import getAllProducts, { ProductEdge } from './get-all-products'
import type { Product } from '@vercel/commerce/types/product'
export default function getCustomerWishlistOperation({
commerce,
@ -47,6 +49,12 @@ export default function getCustomerWishlistOperation({
const wishlist = data[0]
if (!wishlist) {
return {}
}
const items: WishlistItem[] = []
if (includeProducts && wishlist?.items?.length) {
const ids = wishlist.items
?.map((item) => (item?.productId ? String(item?.productId) : null))
@ -57,25 +65,36 @@ export default function getCustomerWishlistOperation({
variables: { first: 50, ids },
config,
})
// Put the products in an object that we can use to get them by id
const productsById = graphqlData.products.reduce<{
[k: number]: ProductEdge
[k: number]: Product
}>((prods, p) => {
prods[Number(p.id)] = p as any
return prods
}, {})
// Populate the wishlist items with the graphql products
wishlist.items.forEach((item) => {
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
items.push({
id: String(item.id),
productId: String(item.productId),
variantId: String(item.variantId),
product,
})
}
})
}
}
return { wishlist: wishlist as RecursiveRequired<typeof wishlist> }
return {
wishlist: {
id: String(wishlist.id),
items,
},
}
}
return getCustomerWishlist

View File

@ -100,7 +100,7 @@ export default function getAllProductPathsOperation({
const variables: GetProductQueryVariables = {
locale,
hasLocale: !!locale,
path: slug ? `/${slug}` : vars.path!,
path: `/${slug}`,
}
const { data } = await config.fetch<GetProductQuery>(query, { variables })
const product = data.site?.route?.node

View File

@ -83,32 +83,38 @@ export interface ProductVariant {
*/
image?: Image
}
/**
*
* The product metafield object, which is a custom field attached to a product. It can be used to store additional information about the product in a structured format.
* @example `reviews`
*/
export interface ProductMetafield {
/**
* The key name for the metafield.
* The unique identifier for the metafield.
*/
key: string
/**
* The namespace for the metafield.
* The namespace for the metafield, which is a container for a set of metadata.
* @example `rating`
*/
namespace: string
/**
* The value of the metafield.
* The value of the metafield, usually a string that can be might parsed into JSON.
* @example `{"value": 5, "scale_max": 5}`
*/
value: any
/**
* Automatically transformed value of the metafield.
* The value of the metafield, complete with HTML formatting.
*/
html?: string
valueHtml?: string
/**
* The type of the metafield.
* @example `date`
* The type of the metafield, used to determine how the value should be interpreted.
* For example: `string`, `integer`, `boolean`, `json` ...
*/
type?: string
@ -119,11 +125,12 @@ export interface ProductMetafield {
}
/**
* Product Metafields, grouped by namespace.
* The namespace is the key of the object, and the value is an object with the metafield key and an object with the metafield data.
* The product metafields are custom fields that can be added to a product. They are used to store additional information about the product.
* @example
* {
* // Namespace, the container for a set of metadata
* reviews: {
* // Key of the metafield, used to differentiate between metafields of the same namespace
* rating: {
* key: 'rating',
* value: 5,
@ -132,10 +139,19 @@ export interface ProductMetafield {
* }
*/
export interface ProductMetafields {
/**
* The namespace for the metafield, which is a container for a set of metadata.
* @example `reviews`, `specifications`
*/
[namespace: string]: {
/**
* The key of the metafield, which is the name of the metafield.
* @example `rating`
*/
[key: string]: ProductMetafield
}
}
export interface Product {
/**
* The unique identifier for the product.
@ -170,8 +186,22 @@ export interface Product {
*/
images: Image[]
/**
* The products metafields. This is a list of metafields that are attached to the product.
*
* The products custom fields. They are used to store simple key-value additional information about the product.
*/
customFields?: Record<string, string>
/**
* The product metafields are advanced custom fields that can be added to a product. They are used to store additional information about the product, usually in a structured format.
* @example
* {
* // Namespace, the container for a set of metadata
* reviews: {
* // Key of the metafield, used to differentiate between metafields of the same namespace
* rating: {
* key: 'rating',
* value: 5,
* // ... other metafield properties
* }
* }
*/
metafields?: ProductMetafields
/**
@ -265,40 +295,24 @@ export type GetAllProductsOperation = {
}
}
export type MetafieldsIdentifiers =
| Record<string, string[]>
| Array<{
namespace: string
key: string
}>
export type MetafieldsIdentifiers = Array<{
namespace: string
key: string
}>
export type GetProductOperation = {
data: { product?: Product }
variables:
| {
path: string
slug?: never
}
| ({
path?: never
slug: string
} & {
/**
* Metafields identifiers used to fetch the product metafields.
* It can be an array of objects with the namespace and key, or an object with the namespace as the key and an array of keys as the value.
*
* @example
* metafields: {
* reviews: ['rating', 'count']
* }
*
* // or
*
* metafields: [
* {namespace: 'reviews', key: 'rating'},
* {namespace: 'reviews', key: 'count'},
* ]
*/
withMetafields?: MetafieldsIdentifiers
})
variables: {
slug: string
/**
* Metafields identifiers used to fetch the product metafields, represented as an array of objects with the namespace and key.
*
* @example
* withMetafields: [
* {namespace: 'reviews', key: 'rating'},
* {namespace: 'reviews', key: 'count'},
* ]
*/
withMetafields?: MetafieldsIdentifiers
}
}

View File

@ -1,30 +1,24 @@
import type { OperationContext } from '@vercel/commerce/api/operations'
import { normalizeProduct } from '../../utils'
import type { GetProductOperation } from '@vercel/commerce/types/product'
import type { Provider, SaleorConfig } from '..'
import { normalizeProduct } from '../../utils'
import * as Query from '../../utils/queries'
type Variables = {
slug: string
}
type ReturnType = {
product: any
}
export default function getProductOperation({
commerce,
}: OperationContext<Provider>) {
async function getProduct({
async function getProduct<T extends GetProductOperation>({
query = Query.ProductOneBySlug,
variables,
config: cfg,
}: {
query?: string
variables: Variables
variables: T['variables']
config?: Partial<SaleorConfig>
preview?: boolean
}): Promise<ReturnType> {
}): Promise<T['data']> {
const { fetch, locale } = commerce.getConfig(cfg)
const { data } = await fetch(
@ -37,9 +31,9 @@ export default function getProductOperation({
}
)
return {
product: data && data.product ? normalizeProduct(data.product) : null,
}
return data && data.product
? { product: normalizeProduct(data.product) }
: {}
}
return getProduct

View File

@ -1,3 +1,5 @@
import type { GetProductOperation } from '@vercel/commerce/types/product'
import { normalizeProduct } from '../../utils'
import { Product } from '@vercel/commerce/types/product'
@ -7,15 +9,15 @@ import { Provider, SwellConfig } from '../'
export default function getProductOperation({
commerce,
}: OperationContext<Provider>) {
async function getProduct({
async function getProduct<T extends GetProductOperation>({
variables,
config: cfg,
}: {
query?: string
variables: { slug: string }
variables: T['variables']
config?: Partial<SwellConfig>
preview?: boolean
}): Promise<Product | {} | any> {
}): Promise<T['data']> {
const config = commerce.getConfig(cfg)
const product = await config.fetch('products', 'get', [variables.slug])
@ -24,9 +26,7 @@ export default function getProductOperation({
product.variants = product.variants?.results
}
return {
product: product ? normalizeProduct(product) : null,
}
return product ? { product: normalizeProduct(product) } : {}
}
return getProduct

View File

@ -1,22 +1,26 @@
import { Product } from '@vercel/commerce/types/product'
import { OperationContext } from '@vercel/commerce/api/operations'
import { Provider, VendureConfig } from '../'
import { GetProductQuery } from '../../../schema'
import type {
Product,
GetProductOperation,
} from '@vercel/commerce/types/product'
import type { OperationContext } from '@vercel/commerce/api/operations'
import type { Provider, VendureConfig } from '../'
import type { GetProductQuery } from '../../../schema'
import { getProductQuery } from '../../utils/queries/get-product-query'
export default function getProductOperation({
commerce,
}: OperationContext<Provider>) {
async function getProduct({
async function getProduct<T extends GetProductOperation>({
query = getProductQuery,
variables,
config: cfg,
}: {
query?: string
variables: { slug: string }
variables: T['variables']
config?: Partial<VendureConfig>
preview?: boolean
}): Promise<Product | {} | any> {
}): Promise<T['data']> {
const config = commerce.getConfig(cfg)
const locale = config.locale

3092
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -94,20 +94,20 @@ const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
<Collapse title="Care">
<Text
className="leading-0"
html={product.metafields.descriptors.care_guide.html}
html={product.metafields.descriptors.care_guide.valueHtml}
/>
</Collapse>
)}
{product.metafields?.my_fields && (
<Collapse title="Details">
{Object.entries(product.metafields.my_fields).map(([_, field]) => (
{Object.values(product.metafields.my_fields).map((field) => (
<div
key={field.key}
className="flex gap-2 border-b py-3 border-accent-2 border-dashed last:border-b-0"
>
<strong className="leading-7">{field.name}:</strong>
<Text html={field.html || field.value} className="!mx-0" />
<Text html={field.valueHtml || field.value} className="!mx-0" />
</div>
))}
</Collapse>

View File

@ -23,8 +23,8 @@
"@components/*": ["components/*"],
"@commerce": ["../packages/commerce/src"],
"@commerce/*": ["../packages/commerce/src/*"],
"@framework": ["../packages/shopify/src"],
"@framework/*": ["../packages/shopify/src/*"]
"@framework": ["../packages/local/src"],
"@framework/*": ["../packages/local/src/*"]
}
},
"include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],