forked from crowetic/commerce
* Initial work, copied from the Shopify provider * Added basis setup and type generation for the products queries * refactor: adjust the types * task: relax the Node.js constraint * fix: page/product properties * disable unknown fields * mention Saleor in the README * setup debugging for Next.js * Check nextjs-commerce bug if no images are added for a product * fix: client/server pecularities for env visibility Must prefix with `NEXT_PUBLIC_` so that the API URL is visible on the client * re: make search work with Saleor API (WIP) * task: update deps * task: move to Webpack 5.x * saleor: initial cart integration * update deps * saleor: shall the cart appear! * task: remove deprecated packages * saleor: adding/removing from the cart * saleor: preliminary signup process * saleor: fix the prices in the cart * update deps * update deps * Added the options for a variant to the product page * Mapped options to variants * Mapped options to variants * saleor: refine the auth process * saleor: remove unused code * saleor: handle customer find via refresh temporary solution * saleor: update deps * saleor: fix the session handling * saleor: fix the variants * saleor: simplify the naming for GraphQL statements * saleor: fix the type for collection * saleor: arrange the error codes * saleor: integrate collections * saleor: fix product sorting * saleor: set cookie location * saleor: update the schema * saleor: attach checkout to customer * saleor: fix the checkout flow * saleor: unify GraphQL naming approach * task: update deps * Add the env variables for saleor to the template * task: prettier * saleor: stub API for build/typescript compilation thanks @cond0r * task: temporarily disable for the `build` * saleor: refactor GraphQL queries * saleor: adjust the config * task: update dependencies * revert: Next.js to `10.0.9` * saleor: fix the checkout fetch query * task: update dependencies * saleor: adapt for displaying featured products * saleor: update the provider structure * saleor: make the home page representable * feature/cart: display the variant name (cond) Co-authored-by: Patryk Zawadzki <patrys@room-303.com> Co-authored-by: royderks <10717410+royderks@users.noreply.github.com>
134 lines
3.8 KiB
TypeScript
134 lines
3.8 KiB
TypeScript
import { Product } from '@commerce/types/product'
|
|
|
|
import { Product as SaleorProduct, Checkout, CheckoutLine, Money, ProductVariant } from '../schema'
|
|
|
|
import type { Cart, LineItem } from '../types'
|
|
|
|
// TODO: Check nextjs-commerce bug if no images are added for a product
|
|
const placeholderImg = '/product-img-placeholder.svg'
|
|
|
|
const money = ({ amount, currency }: Money) => {
|
|
return {
|
|
value: +amount,
|
|
currencyCode: currency || 'USD',
|
|
}
|
|
}
|
|
|
|
const normalizeProductOptions = (options: ProductVariant[]) => {
|
|
return options
|
|
?.map((option) => option?.attributes)
|
|
.flat(1)
|
|
.reduce<any>((acc, x) => {
|
|
if (acc.find(({ displayName }: any) => displayName === x.attribute.name)) {
|
|
return acc.map((opt: any) => {
|
|
return opt.displayName === x.attribute.name
|
|
? {
|
|
...opt,
|
|
values: [
|
|
...opt.values,
|
|
...x.values.map((value: any) => ({
|
|
label: value?.name,
|
|
})),
|
|
],
|
|
}
|
|
: opt
|
|
})
|
|
}
|
|
|
|
return acc.concat({
|
|
__typename: 'MultipleChoiceOption',
|
|
displayName: x.attribute.name,
|
|
variant: 'size',
|
|
values: x.values.map((value: any) => ({
|
|
label: value?.name,
|
|
})),
|
|
})
|
|
}, [])
|
|
}
|
|
|
|
const normalizeProductVariants = (variants: ProductVariant[]) => {
|
|
return variants?.map((variant) => {
|
|
const { id, sku, name, pricing } = variant
|
|
const price = pricing?.price?.net && money(pricing.price.net)?.value
|
|
|
|
return {
|
|
id,
|
|
name,
|
|
sku: sku ?? id,
|
|
price,
|
|
listPrice: price,
|
|
requiresShipping: true,
|
|
options: normalizeProductOptions([variant]),
|
|
}
|
|
})
|
|
}
|
|
|
|
export function normalizeProduct(productNode: SaleorProduct): Product {
|
|
const { id, name, media = [], variants, description, slug, pricing, ...rest } = productNode
|
|
|
|
const product = {
|
|
id,
|
|
name,
|
|
vendor: '',
|
|
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)) || {
|
|
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[]) : [],
|
|
...rest,
|
|
}
|
|
|
|
return product as Product
|
|
}
|
|
|
|
export function normalizeCart(checkout: Checkout): Cart {
|
|
const lines = checkout.lines as CheckoutLine[]
|
|
const lineItems: LineItem[] = lines.length > 0 ? lines?.map<LineItem>(normalizeLineItem) : []
|
|
|
|
return {
|
|
id: checkout.id,
|
|
customerId: '',
|
|
email: '',
|
|
createdAt: checkout.created,
|
|
currency: {
|
|
code: checkout.totalPrice?.currency!,
|
|
},
|
|
taxesIncluded: false,
|
|
lineItems,
|
|
lineItemsSubtotalPrice: checkout.subtotalPrice?.gross?.amount!,
|
|
subtotalPrice: checkout.subtotalPrice?.gross?.amount!,
|
|
totalPrice: checkout.totalPrice?.gross.amount!,
|
|
discounts: [],
|
|
}
|
|
}
|
|
|
|
function normalizeLineItem({ id, variant, quantity }: CheckoutLine): LineItem {
|
|
return {
|
|
id,
|
|
variantId: String(variant?.id),
|
|
productId: String(variant?.id),
|
|
name: `${variant.product.name}`,
|
|
quantity,
|
|
variant: {
|
|
id: String(variant?.id),
|
|
sku: variant?.sku ?? '',
|
|
name: variant?.name!,
|
|
image: {
|
|
url: variant?.media![0] ? variant?.media![0].url : placeholderImg,
|
|
},
|
|
requiresShipping: false,
|
|
price: variant?.pricing?.price?.gross.amount!,
|
|
listPrice: 0,
|
|
},
|
|
path: String(variant?.product?.slug),
|
|
discounts: [],
|
|
options: [],
|
|
}
|
|
}
|