4
0
forked from crowetic/commerce

Match product options with variants

This commit is contained in:
Greg Hoskin 2021-04-21 19:56:00 -05:00
parent 8a8ef7dbba
commit dd40b8c604
4 changed files with 196 additions and 143 deletions

View File

@ -1,5 +1,4 @@
import { getConfig, SwellConfig } from '../api'
import getPageQuery from '../utils/queries/get-page-query'
import { Page } from './get-all-pages'
type Variables = {

View File

@ -321,96 +321,113 @@ export enum CardBrand {
}
/** A container for all the information required to checkout items and pay. */
export type Checkout = Node & {
__typename?: 'Checkout'
/** The gift cards used on the checkout. */
appliedGiftCards: Array<AppliedGiftCard>
/**
* The available shipping rates for this Checkout.
* Should only be used when checkout `requiresShipping` is `true` and
* the shipping address is valid.
*/
availableShippingRates?: Maybe<AvailableShippingRates>
/** The date and time when the checkout was completed. */
completedAt?: Maybe<Scalars['DateTime']>
/** The date and time when the checkout was created. */
createdAt: Scalars['DateTime']
/** The currency code for the Checkout. */
currencyCode: CurrencyCode
/** A list of extra information that is added to the checkout. */
customAttributes: Array<Attribute>
/**
* The customer associated with the checkout.
* @deprecated This field will always return null. If you have an authentication token for the customer, you can use the `customer` field on the query root to retrieve it.
*/
customer?: Maybe<Customer>
/** Discounts that have been applied on the checkout. */
discountApplications: DiscountApplicationConnection
/** The email attached to this checkout. */
email?: Maybe<Scalars['String']>
/** Globally unique identifier. */
id: Scalars['ID']
/** A list of line item objects, each one containing information about an item in the checkout. */
lineItems: CheckoutLineItemConnection
/** The sum of all the prices of all the items in the checkout. Duties, taxes, shipping and discounts excluded. */
lineItemsSubtotalPrice: MoneyV2
/** The note associated with the checkout. */
note?: Maybe<Scalars['String']>
/** The resulting order from a paid checkout. */
order?: Maybe<Order>
/** The Order Status Page for this Checkout, null when checkout is not completed. */
orderStatusUrl?: Maybe<Scalars['URL']>
/**
* The amount left to be paid. This is equal to the cost of the line items, taxes and shipping minus discounts and gift cards.
* @deprecated Use `paymentDueV2` instead
*/
paymentDue: Scalars['Money']
/** The amount left to be paid. This is equal to the cost of the line items, duties, taxes and shipping minus discounts and gift cards. */
paymentDueV2: MoneyV2
/**
* Whether or not the Checkout is ready and can be completed. Checkouts may
* have asynchronous operations that can take time to finish. If you want
* to complete a checkout or ensure all the fields are populated and up to
* date, polling is required until the value is true.
*/
ready: Scalars['Boolean']
/** States whether or not the fulfillment requires shipping. */
requiresShipping: Scalars['Boolean']
/** The shipping address to where the line items will be shipped. */
shippingAddress?: Maybe<MailingAddress>
/** The discounts that have been allocated onto the shipping line by discount applications. */
shippingDiscountAllocations: Array<DiscountAllocation>
/** Once a shipping rate is selected by the customer it is transitioned to a `shipping_line` object. */
shippingLine?: Maybe<ShippingRate>
/**
* Price of the checkout before shipping and taxes.
* @deprecated Use `subtotalPriceV2` instead
*/
subtotalPrice: Scalars['Money']
/** Price of the checkout before duties, shipping and taxes. */
subtotalPriceV2: MoneyV2
/** Specifies if the Checkout is tax exempt. */
taxExempt: Scalars['Boolean']
/** Specifies if taxes are included in the line item and shipping line prices. */
taxesIncluded: Scalars['Boolean']
/**
* The sum of all the prices of all the items in the checkout, taxes and discounts included.
* @deprecated Use `totalPriceV2` instead
*/
totalPrice: Scalars['Money']
/** The sum of all the prices of all the items in the checkout, duties, taxes and discounts included. */
totalPriceV2: MoneyV2
/**
* The sum of all the taxes applied to the line items and shipping lines in the checkout.
* @deprecated Use `totalTaxV2` instead
*/
totalTax: Scalars['Money']
/** The sum of all the taxes applied to the line items and shipping lines in the checkout. */
totalTaxV2: MoneyV2
/** The date and time when the checkout was last updated. */
updatedAt: Scalars['DateTime']
/** The url pointing to the checkout accessible from the web. */
webUrl: Scalars['URL']
export type Checkout = {
name: string
currency: string
support_email: string
fields: any[]
scripts: any[]
accounts: string
email_optin: boolean
terms_policy?: string
refund_policy?: string
privacy_policy?: string
theme?: stirng
countries: any[]
currencies: any[]
payment_methods: any[]
coupons: boolean
giftcards: boolean
// __typename?: 'Checkout'
// /** The gift cards used on the checkout. */
// appliedGiftCards: Array<AppliedGiftCard>
// /**
// * The available shipping rates for this Checkout.
// * Should only be used when checkout `requiresShipping` is `true` and
// * the shipping address is valid.
// */
// availableShippingRates?: Maybe<AvailableShippingRates>
// /** The date and time when the checkout was completed. */
// completedAt?: Maybe<Scalars['DateTime']>
// /** The date and time when the checkout was created. */
// createdAt: Scalars['DateTime']
// /** The currency code for the Checkout. */
// currencyCode: CurrencyCode
// /** A list of extra information that is added to the checkout. */
// customAttributes: Array<Attribute>
// /**
// * The customer associated with the checkout.
// * @deprecated This field will always return null. If you have an authentication token for the customer, you can use the `customer` field on the query root to retrieve it.
// */
// customer?: Maybe<Customer>
// /** Discounts that have been applied on the checkout. */
// discountApplications: DiscountApplicationConnection
// /** The email attached to this checkout. */
// email?: Maybe<Scalars['String']>
// /** Globally unique identifier. */
// id: Scalars['ID']
// /** A list of line item objects, each one containing information about an item in the checkout. */
// lineItems: CheckoutLineItemConnection
// /** The sum of all the prices of all the items in the checkout. Duties, taxes, shipping and discounts excluded. */
// lineItemsSubtotalPrice: MoneyV2
// /** The note associated with the checkout. */
// note?: Maybe<Scalars['String']>
// /** The resulting order from a paid checkout. */
// order?: Maybe<Order>
// /** The Order Status Page for this Checkout, null when checkout is not completed. */
// orderStatusUrl?: Maybe<Scalars['URL']>
// /**
// * The amount left to be paid. This is equal to the cost of the line items, taxes and shipping minus discounts and gift cards.
// * @deprecated Use `paymentDueV2` instead
// */
// paymentDue: Scalars['Money']
// /** The amount left to be paid. This is equal to the cost of the line items, duties, taxes and shipping minus discounts and gift cards. */
// paymentDueV2: MoneyV2
// /**
// * Whether or not the Checkout is ready and can be completed. Checkouts may
// * have asynchronous operations that can take time to finish. If you want
// * to complete a checkout or ensure all the fields are populated and up to
// * date, polling is required until the value is true.
// */
// ready: Scalars['Boolean']
// /** States whether or not the fulfillment requires shipping. */
// requiresShipping: Scalars['Boolean']
// /** The shipping address to where the line items will be shipped. */
// shippingAddress?: Maybe<MailingAddress>
// /** The discounts that have been allocated onto the shipping line by discount applications. */
// shippingDiscountAllocations: Array<DiscountAllocation>
// /** Once a shipping rate is selected by the customer it is transitioned to a `shipping_line` object. */
// shippingLine?: Maybe<ShippingRate>
// /**
// * Price of the checkout before shipping and taxes.
// * @deprecated Use `subtotalPriceV2` instead
// */
// subtotalPrice: Scalars['Money']
// /** Price of the checkout before duties, shipping and taxes. */
// subtotalPriceV2: MoneyV2
// /** Specifies if the Checkout is tax exempt. */
// taxExempt: Scalars['Boolean']
// /** Specifies if taxes are included in the line item and shipping line prices. */
// taxesIncluded: Scalars['Boolean']
// /**
// * The sum of all the prices of all the items in the checkout, taxes and discounts included.
// * @deprecated Use `totalPriceV2` instead
// */
// totalPrice: Scalars['Money']
// /** The sum of all the prices of all the items in the checkout, duties, taxes and discounts included. */
// totalPriceV2: MoneyV2
// /**
// * The sum of all the taxes applied to the line items and shipping lines in the checkout.
// * @deprecated Use `totalTaxV2` instead
// */
// totalTax: Scalars['Money']
// /** The sum of all the taxes applied to the line items and shipping lines in the checkout. */
// totalTaxV2: MoneyV2
// /** The date and time when the checkout was last updated. */
// updatedAt: Scalars['DateTime']
// /** The url pointing to the checkout accessible from the web. */
// webUrl: Scalars['URL']
}
/** A container for all the information required to checkout items and pay. */

View File

@ -1,6 +1,15 @@
import * as Core from '@commerce/types'
import { CheckoutLineItem } from './schema'
export type SwellImage = {
file: {
url: String
height: Number
width: Number
}
id: string
}
export type SwellCart = {
id: string
account_id: number
@ -21,9 +30,28 @@ export type SwellCart = {
// TODO: add missing fields
}
export type VariantResult = {
export type SwellVariant = {
id: string
option_value_ids: string[]
name: string
price?: number
stock_status?: string
}
export interface ProductOptionValue {
label: string
hexColors?: string[]
id: string
}
export type ProductOptions = {
id: string
name: string
variant: boolean
values: ProductOptionValue[]
required: boolean
active: boolean
attribute_id: string
}
export interface SwellProduct {

View File

@ -1,19 +1,22 @@
import { Product } from '@commerce/types'
import { Customer } from '@commerce/types'
import { Product, Customer } from '@commerce/types'
import { fileURLToPath } from 'node:url'
import {
Product as ShopifyProduct,
Checkout,
CheckoutLineItemEdge,
SelectedOption,
ImageConnection,
ProductVariantConnection,
MoneyV2,
ProductOption,
} from '../schema'
import type { Cart, LineItem, SwellCustomer, SwellProduct } from '../types'
import type {
Cart,
LineItem,
SwellCustomer,
SwellProduct,
SwellImage,
SwellVariant,
ProductOptionValue,
} from '../types'
const money = ({ amount, currencyCode }: MoneyV2) => {
return {
@ -22,15 +25,22 @@ const money = ({ amount, currencyCode }: MoneyV2) => {
}
}
type normalizedProductOption = {
__typename?: string
id: string
displayName: string
values: ProductOptionValue[]
}
const normalizeProductOption = ({
id,
name: displayName,
name: displayName = '',
values,
}: ProductOption) => {
let returnValues = values.map((value) => {
let output: any = {
displayName,
label: value.name,
id: value?.id || id,
}
if (displayName === 'Color') {
output = {
@ -48,50 +58,52 @@ const normalizeProductOption = ({
}
}
type SwellImage = {
file: {
url: String
height: Number
width: Number
}
id: string
}
const normalizeProductImages = (images) => {
const normalizeProductImages = (images: SwellImage[]) => {
if (!images) {
return [{ url: '/' }]
}
return images?.map(({ file, ...rest }: SwellImage) => ({
url: file?.url,
height: file?.height,
width: file?.width,
url: file?.url + '',
height: Number(file?.height),
width: Number(file?.width),
...rest,
}))
}
const normalizeProductVariants = (variants) => {
return variants?.map(({ id, name, price, sku }) => {
const values = name
.split(',')
.map((i) => ({ name: i.trim(), label: i.trim() }))
const normalizeProductVariants = (
variants: SwellVariant[],
productOptions: normalizedProductOption[]
) => {
return variants?.map(
({ id, name, price, option_value_ids: optionValueIds }) => {
const values = name
.split(',')
.map((i) => ({ name: i.trim(), label: i.trim() }))
const options = values.map((value) => {
return normalizeProductOption({
const options = optionValueIds.map((id) => {
const matchingOption = productOptions.find((option) => {
return option.values.find(
(value: ProductOptionValue) => value.id == id
)
})
return normalizeProductOption({
id,
name: matchingOption?.displayName ?? '',
values,
})
})
return {
id,
name,
values: [value],
})
})
return {
id,
name,
sku: sku ?? id,
price: price ?? null,
listPrice: price ?? null,
// requiresShipping: true,
options,
// sku: sku ?? id,
price: price ?? null,
listPrice: price ?? null,
// requiresShipping: true,
options,
}
}
})
)
}
export function normalizeProduct(swellProduct: SwellProduct): Product {
@ -108,10 +120,10 @@ export function normalizeProduct(swellProduct: SwellProduct): Product {
const productOptions = options
? options.map((o) => normalizeProductOption(o))
: []
const productVariants = variants ? normalizeProductVariants(variants) : []
const productVariants = variants
? normalizeProductVariants(variants, productOptions)
: []
// ProductView.tsx assumes the existence of at least one product variant
const emptyVariants = [{ options: [{ id: 123 }] }]
const productImages = normalizeProductImages(images)
const product = {
...swellProduct,
@ -120,10 +132,7 @@ export function normalizeProduct(swellProduct: SwellProduct): Product {
vendor: '',
path: `/${slug}`,
images: productImages,
variants:
productVariants && productVariants.length
? productVariants
: emptyVariants,
variants: productVariants,
options: productOptions,
price: {
value,
@ -167,12 +176,12 @@ function normalizeLineItem({
}: CheckoutLineItemEdge): LineItem {
const item = {
id,
variantId: String(variant?.id),
productId: String(product?.id),
variantId: variant?.id ?? '',
productId: product?.id ?? '',
name: product?.name ?? '',
quantity,
variant: {
id: String(variant?.id),
id: variant?.id ?? '',
sku: variant?.sku ?? '',
name: variant?.name!,
image: {