commerce/lib/fourthwall/reshape.ts
2024-09-20 10:40:06 -04:00

176 lines
4.3 KiB
TypeScript

import { Cart, CartItem, Image, Money, Product, ProductVariant } from "lib/types";
import { FourthwallCart, FourthwallCartItem, FourthwallMoney, FourthwallProduct, FourthwallProductImage, FourthwallProductVariant } from "./types";
/**
* Utils
*/
const DEFAULT_IMAGE: Image = {
url: '',
altText: '',
width: 0,
height: 0
}
const reshapeMoney = (money: FourthwallMoney): Money => {
return {
amount: money.value.toString(),
currencyCode: money.currency
};
}
/**
* Products
*/
export const reshapeProducts = (products: FourthwallProduct[]) => {
const reshapedProducts = [];
for (const product of products) {
if (product) {
const reshapedProduct = reshapeProduct(product);
if (reshapedProduct) {
reshapedProducts.push(reshapedProduct);
}
}
}
return reshapedProducts;
};
export const reshapeProduct = (product: FourthwallProduct): Product | undefined => {
if (!product) {
return undefined;
}
const { images, variants, ...rest } = product;
const minPrice = Math.min(...variants.map((v) => v.unitPrice.value));
const maxPrice = Math.max(...variants.map((v) => v.unitPrice.value));
const currencyCode = variants[0]?.unitPrice.currency || 'USD';
const sizes = new Set(variants.map((v) => v.attributes.size.name));
const colors = new Set(variants.map((v) => v.attributes.color.name));
return {
...rest,
handle: product.slug,
title: product.name,
descriptionHtml: product.description,
description: product.description,
images: reshapeImages(images, product.name),
variants: reshapeVariants(variants),
priceRange: {
minVariantPrice: {
amount: minPrice.toString(),
currencyCode,
},
maxVariantPrice: {
amount: maxPrice.toString(),
currencyCode,
}
},
featuredImage: reshapeImages(images, product.name)[0] || DEFAULT_IMAGE,
options: [{
id: 'color',
name: 'Color',
values: [...colors]
}, {
id: 'size',
name: 'Size',
values: [...sizes]
}],
// TODO: stubbed out
availableForSale: true,
seo: {
title: product.name,
description: product.description,
},
tags: [],
updatedAt: new Date().toISOString(),
};
};
const reshapeImages = (images: FourthwallProductImage[], productTitle: string): Image[] => {
return images.map((image) => {
const filename = image.url.match(/.*\/(.*)\..*/)?.[1];
return {
...image,
altText: `${productTitle} - ${filename}`
};
});
};
const reshapeVariants = (variants: FourthwallProductVariant[]): ProductVariant[] => {
return variants.map((v) => ({
id: v.id,
title: v.name,
availableForSale: true,
images: reshapeImages(v.images, v.name),
selectedOptions: [{
name: 'Size',
value: v.attributes.size.name
}, {
name: 'Color',
value: v.attributes.color.name
}],
price: reshapeMoney(v.unitPrice),
}))
}
/**
* Cart
*/
const reshapeCartItem = (item: FourthwallCartItem): CartItem => {
return {
id: item.variant.id,
quantity: item.quantity,
cost: {
totalAmount: reshapeMoney(item.variant.unitPrice)
},
merchandise: {
id: item.variant.id,
title: item.variant.name,
// TODO: Stubbed out
selectedOptions: [],
product: {
// TODO: need this product info in model
id: 'TT',
handle: 'TT',
title: 'TT',
featuredImage: {
url: item.variant.images[0]?.url || 'TT',
altText: 'TT',
width: item.variant.images[0]?.width || 100,
height: item.variant.images[0]?.height || 100
}
}
}
};
}
export const reshapeCart = (cart: FourthwallCart): Cart => {
const totalValue = cart.items.map((item) => item.quantity * item.variant.unitPrice.value).reduce((a, b) => a + b, 0);
const currencyCode = cart.items[0]?.variant.unitPrice.currency || 'USD';
return {
...cart,
cost: {
totalAmount: {
amount: totalValue.toString(),
currencyCode,
},
subtotalAmount: {
amount: totalValue.toString(),
currencyCode,
},
},
lines: cart.items.map(reshapeCartItem),
currency: currencyCode,
// TODO: Stubbed out
checkoutUrl: 'TT',
totalQuantity: cart.items.map((item) => item.quantity).reduce((a, b) => a + b, 0)
};
};