mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 20:26:49 +00:00
fix: add on products on cart
Signed-off-by: Chloe <pinkcloudvnn@gmail.com>
This commit is contained in:
@@ -53,3 +53,8 @@ export const CONDITIONS = {
|
||||
};
|
||||
|
||||
export const DELIVERY_OPTION_KEY = 'delivery';
|
||||
|
||||
export const ADD_ON_PRODUCT_TYPES = {
|
||||
addOn: 'Add On',
|
||||
coreCharge: 'Core Charge'
|
||||
};
|
||||
|
@@ -1,9 +1,13 @@
|
||||
import productFragment from './product';
|
||||
import imageFragment from './image';
|
||||
|
||||
const cartFragment = /* GraphQL */ `
|
||||
fragment cart on Cart {
|
||||
id
|
||||
checkoutUrl
|
||||
attributes {
|
||||
key
|
||||
value
|
||||
}
|
||||
cost {
|
||||
subtotalAmount {
|
||||
amount
|
||||
@@ -38,11 +42,22 @@ const cartFragment = /* GraphQL */ `
|
||||
value
|
||||
}
|
||||
product {
|
||||
...product
|
||||
featuredImage {
|
||||
...image
|
||||
}
|
||||
handle
|
||||
title
|
||||
productType
|
||||
}
|
||||
coreVariantId: metafield(key: "coreVariant", namespace: "custom") {
|
||||
value
|
||||
}
|
||||
addOnQuantity: metafield(namespace: "custom", key: "add_on_quantity") {
|
||||
value
|
||||
}
|
||||
addOnProductId: metafield(namespace: "custom", key: "add_on") {
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +65,7 @@ const cartFragment = /* GraphQL */ `
|
||||
}
|
||||
totalQuantity
|
||||
}
|
||||
${productFragment}
|
||||
${imageFragment}
|
||||
`;
|
||||
|
||||
export default cartFragment;
|
||||
|
@@ -64,6 +64,12 @@ const productFragment = /* GraphQL */ `
|
||||
condition: metafield(namespace: "custom", key: "condition") {
|
||||
value
|
||||
}
|
||||
addOnQuantity: metafield(namespace: "custom", key: "add_on_quantity") {
|
||||
value
|
||||
}
|
||||
addOnProductId: metafield(namespace: "custom", key: "add_on") {
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
ADD_ON_PRODUCT_TYPES,
|
||||
AVAILABILITY_FILTER_ID,
|
||||
HIDDEN_PRODUCT_TAG,
|
||||
MAKE_FILTER_ID,
|
||||
@@ -40,7 +41,6 @@ import {
|
||||
import {
|
||||
Cart,
|
||||
CartAttributeInput,
|
||||
CartItem,
|
||||
Collection,
|
||||
Connection,
|
||||
Filter,
|
||||
@@ -161,7 +161,7 @@ const reshapeCart = (cart: ShopifyCart): Cart => {
|
||||
...lineItem,
|
||||
merchandise: {
|
||||
...lineItem.merchandise,
|
||||
product: reshapeProduct(lineItem.merchandise.product)
|
||||
product: lineItem.merchandise.product
|
||||
}
|
||||
}))
|
||||
};
|
||||
@@ -277,14 +277,22 @@ const reshapeImages = (images: Connection<Image>, productTitle: string) => {
|
||||
};
|
||||
|
||||
const reshapeVariants = (variants: ShopifyProductVariant[]): ProductVariant[] => {
|
||||
return variants.map((variant) => ({
|
||||
return variants.map(({ addOnProductId, addOnQuantity, ...variant }) => ({
|
||||
...variant,
|
||||
waiverAvailable: parseMetaFieldValue<boolean>(variant.waiverAvailable),
|
||||
coreVariantId: variant.coreVariantId?.value || null,
|
||||
coreCharge: parseMetaFieldValue<Money>(variant.coreCharge),
|
||||
mileage: variant.mileage?.value ?? null,
|
||||
estimatedDelivery: variant.estimatedDelivery?.value || null,
|
||||
condition: variant.condition?.value || null
|
||||
condition: variant.condition?.value || null,
|
||||
...(addOnProductId
|
||||
? {
|
||||
addOnProduct: {
|
||||
id: addOnProductId.value,
|
||||
quantity: addOnQuantity?.value ? Number(addOnQuantity.value) : 1
|
||||
}
|
||||
}
|
||||
: {})
|
||||
}));
|
||||
};
|
||||
|
||||
@@ -410,20 +418,34 @@ export async function getCart(cartId: string): Promise<Cart | undefined> {
|
||||
const cart = reshapeCart(res.body.data.cart);
|
||||
|
||||
// attach core charge as an additional attribute of a cart line, and remove the core charge line from cart
|
||||
const extendedCartLines = cart?.lines.reduce((lines, item) => {
|
||||
const coreVariantId = item.merchandise.coreVariantId?.value;
|
||||
if (coreVariantId) {
|
||||
const relatedCoreCharge = cart.lines.find((line) => line.merchandise.id === coreVariantId);
|
||||
return lines.concat([
|
||||
{
|
||||
...item,
|
||||
coreCharge: relatedCoreCharge
|
||||
}
|
||||
]);
|
||||
}
|
||||
const extendedCartLines = cart?.lines
|
||||
.map((item) => {
|
||||
const coreVariantId = item.merchandise.coreVariantId?.value;
|
||||
const addOnProductId = item.merchandise.addOnProductId;
|
||||
const _item = { ...item };
|
||||
|
||||
return lines;
|
||||
}, [] as CartItem[]);
|
||||
if (coreVariantId) {
|
||||
const relatedCoreCharge = cart.lines.find((line) => line.merchandise.id === coreVariantId);
|
||||
_item.coreCharge = relatedCoreCharge;
|
||||
}
|
||||
|
||||
if (addOnProductId) {
|
||||
const relatedAddOnProduct = cart.lines.find(
|
||||
(line) => line.merchandise.id === addOnProductId.value
|
||||
);
|
||||
_item.addOnProduct = relatedAddOnProduct
|
||||
? {
|
||||
...relatedAddOnProduct,
|
||||
quantity: item.merchandise.addOnQuantity
|
||||
? Number(item.merchandise.addOnQuantity.value)
|
||||
: 1
|
||||
}
|
||||
: undefined;
|
||||
}
|
||||
return _item;
|
||||
})
|
||||
// core charge shouldn't present as a dedicated product as it's tightly coupled with the product
|
||||
.filter((item) => item.merchandise.product.productType !== ADD_ON_PRODUCT_TYPES.coreCharge);
|
||||
|
||||
const totalQuantity = extendedCartLines.reduce((sum, line) => sum + line.quantity, 0);
|
||||
|
||||
|
@@ -25,10 +25,19 @@ export type CartItem = {
|
||||
name: string;
|
||||
value: string;
|
||||
}[];
|
||||
product: Product;
|
||||
product: {
|
||||
id: string;
|
||||
handle: string;
|
||||
title: string;
|
||||
featuredImage: Image;
|
||||
productType: string;
|
||||
};
|
||||
coreVariantId: { value: string } | null;
|
||||
addOnQuantity: { value: string } | null;
|
||||
addOnProductId: { value: string } | null;
|
||||
};
|
||||
coreCharge?: CartItem;
|
||||
addOnProduct?: CartItem & { quantity: number };
|
||||
};
|
||||
|
||||
export type Collection = Omit<ShopifyCollection, 'helpfulLinks'> & {
|
||||
@@ -151,6 +160,10 @@ export type ProductVariant = {
|
||||
condition: string | null;
|
||||
engineCylinders: string | null;
|
||||
fuelType: string | null;
|
||||
addOnProduct?: {
|
||||
quantity: number;
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type ShopifyCartProductVariant = {
|
||||
@@ -169,7 +182,13 @@ export type CartProductVariant = Omit<ShopifyCartProductVariant, 'coreVariantId'
|
||||
|
||||
export type ShopifyProductVariant = Omit<
|
||||
ProductVariant,
|
||||
'coreCharge' | 'waiverAvailable' | 'coreVariantId' | 'mileage' | 'estimatedDelivery' | 'condition'
|
||||
| 'coreCharge'
|
||||
| 'waiverAvailable'
|
||||
| 'coreVariantId'
|
||||
| 'mileage'
|
||||
| 'estimatedDelivery'
|
||||
| 'condition'
|
||||
| 'addOnProduct'
|
||||
> & {
|
||||
waiverAvailable: { value: string };
|
||||
coreVariantId: { value: string } | null;
|
||||
@@ -177,6 +196,8 @@ export type ShopifyProductVariant = Omit<
|
||||
mileage: { value: number } | null;
|
||||
estimatedDelivery: { value: string } | null;
|
||||
condition: { value: string } | null;
|
||||
addOnProductId: { value: string } | null;
|
||||
addOnQuantity: { value: string } | null;
|
||||
};
|
||||
|
||||
export type SEO = {
|
||||
@@ -187,6 +208,7 @@ export type SEO = {
|
||||
export type ShopifyCart = {
|
||||
id: string;
|
||||
checkoutUrl: string;
|
||||
attributes: { key: string; value: string }[];
|
||||
cost: {
|
||||
subtotalAmount: Money;
|
||||
totalAmount: Money;
|
||||
|
Reference in New Issue
Block a user