feat: product options and variants

This commit is contained in:
Victor Gerbrands 2023-05-03 14:01:34 +02:00
parent bff927e700
commit e0e67f4270
3 changed files with 113 additions and 37 deletions

View File

@ -1,7 +1,10 @@
import Link from 'next/link'; import Link from 'next/link';
import Cart from 'components/cart';
import CartIcon from 'components/icons/cart';
import LogoIcon from 'components/icons/logo'; import LogoIcon from 'components/icons/logo';
import { Menu } from 'lib/medusa/types'; import { Menu } from 'lib/medusa/types';
import { Suspense } from 'react';
import MobileMenu from './mobile-menu'; import MobileMenu from './mobile-menu';
import Search from './search'; import Search from './search';
@ -40,10 +43,10 @@ export default async function Navbar() {
</div> </div>
<div className="flex w-1/3 justify-end"> <div className="flex w-1/3 justify-end">
{/* <Suspense fallback={<CartIcon className="h-6" />}> */} <Suspense fallback={<CartIcon className="h-6" />}>
{/* @ts-expect-error Server Component */} {/* @ts-expect-error Server Component */}
{/* <Cart /> */} <Cart />
{/* </Suspense> */} </Suspense>
</div> </div>
</nav> </nav>
); );

View File

@ -4,8 +4,12 @@ import {
MedusaCart, MedusaCart,
MedusaProduct, MedusaProduct,
MedusaProductCollection, MedusaProductCollection,
MedusaProductOption,
MedusaProductVariant,
Product, Product,
ProductCollection ProductCollection,
ProductOption,
ProductVariant
} from './types'; } from './types';
// const endpoint = `${process.env.MEDUSA_BACKEND_API!}`; // const endpoint = `${process.env.MEDUSA_BACKEND_API!}`;
@ -80,6 +84,11 @@ const reshapeProduct = (product: MedusaProduct): Product => {
altText: product.images?.[0]?.id ?? '' altText: product.images?.[0]?.id ?? ''
}; };
const availableForSale = true; const availableForSale = true;
const variants = product.variants.map((variant) => reshapeProductVariant(variant));
let options;
product.options && (options = product.options.map((option) => reshapeProductOption(option)));
// console.log({ options });
return { return {
...product, ...product,
@ -88,7 +97,43 @@ const reshapeProduct = (product: MedusaProduct): Product => {
updatedAt, updatedAt,
tags, tags,
descriptionHtml, descriptionHtml,
availableForSale availableForSale,
options,
variants
};
};
const reshapeProductOption = (productOption: MedusaProductOption): ProductOption => {
const availableForSale = true;
const name = productOption.title;
let values = productOption.values?.map((option) => option.value) || [];
values = [...new Set(values)];
return {
...productOption,
availableForSale,
name,
values
};
};
const reshapeProductVariant = (productVariant: MedusaProductVariant): ProductVariant => {
console.log({ productVariant });
const availableForSale = !!productVariant.inventory_quantity;
const selectedOptions =
productVariant.options?.map((option) => ({
name: option.option?.title ?? '',
value: option.value
})) || [];
const price = {
amount: productVariant.prices?.[0]?.amount.toString() ?? '',
currencyCode: productVariant.prices?.[0]?.currency_code ?? ''
};
return {
...productVariant,
availableForSale,
selectedOptions,
price
}; };
}; };
@ -193,8 +238,9 @@ export async function getCollections(): Promise<ProductCollection[]> {
} }
export async function getProduct(handle: string): Promise<Product | undefined> { export async function getProduct(handle: string): Promise<Product | undefined> {
const res = await medusaRequest('get', `/products?handle=${handle}&limit=1`); const res = await medusaRequest('GET', `/products?handle=${handle}&limit=1`);
return res.body.product; const product = res.body.products[0];
return reshapeProduct(product);
} }
export async function getProducts({ export async function getProducts({

View File

@ -29,8 +29,8 @@ export type MedusaProduct = {
status?: 'draft' | 'proposed' | 'published' | 'rejected'; status?: 'draft' | 'proposed' | 'published' | 'rejected';
images?: Array<MedusaImage>; images?: Array<MedusaImage>;
thumbnail?: string | null; thumbnail?: string | null;
options?: Array<ProductOption>; options?: Array<MedusaProductOption>;
variants: Array<ProductVariant>; variants: Array<MedusaProductVariant>;
categories?: Array<ProductCategory>; categories?: Array<ProductCategory>;
profile_id?: string | null; profile_id?: string | null;
profile?: ShippingProfile | null; profile?: ShippingProfile | null;
@ -52,7 +52,7 @@ export type MedusaProduct = {
tags?: ProductTag[]; tags?: ProductTag[];
}; };
export type Product = Omit<MedusaProduct, 'tags'> & { export type Product = Omit<MedusaProduct, 'tags' | 'options' | 'variants'> & {
featuredImage: FeaturedImage; featuredImage: FeaturedImage;
seo?: { seo?: {
title?: string; title?: string;
@ -68,6 +68,8 @@ export type Product = Omit<MedusaProduct, 'tags'> & {
descriptionHtml: string; descriptionHtml: string;
tags: Array<string>; tags: Array<string>;
availableForSale: boolean; availableForSale: boolean;
options?: Array<ProductOption>;
variants: Array<ProductVariant>;
}; };
export type FeaturedImage = { export type FeaturedImage = {
@ -127,7 +129,7 @@ export type ProductCategory = {
updated_at: string; updated_at: string;
}; };
export type ProductVariant = { export type MedusaProductVariant = {
id: string; id: string;
title?: string; title?: string;
product_id: string; product_id: string;
@ -155,6 +157,51 @@ export type ProductVariant = {
deleted_at: string | null; deleted_at: string | null;
}; };
export type ProductVariant = MedusaProductVariant & {
availableForSale: boolean;
selectedOptions: {
name: string;
value: string;
}[];
price: Money;
};
export type MedusaProductOption = {
id: string;
title: string;
values?: ProductOptionValue[];
product_id: string;
product?: Product | null;
created_at: string;
updated_at: string;
deleted_at?: string | null;
metadata?: Record<string, any> | null;
};
export type ProductOption = Omit<MedusaProductOption, 'values'> & {
availableForSale: boolean;
name: string;
values: string[];
};
export type ProductOptionValue = {
id: string;
value: string;
option_id: string;
option?: MedusaProductOption | null;
variant_id: string;
variant?: MedusaProductVariant | null;
created_at: string;
updated_at: string;
deleted_at?: string | null;
metadata?: Record<string, any> | null;
};
export type Money = {
amount: string;
currencyCode: string;
};
type MoneyAmount = { type MoneyAmount = {
id: string; id: string;
currency_code: string; currency_code: string;
@ -202,31 +249,6 @@ export type CustomerGroup = {
name: string; name: string;
}; };
export type ProductOption = {
id: string;
title: string;
values?: ProductOptionValue[];
product_id: string;
product?: Product | null;
created_at: string;
updated_at: string;
deleted_at?: string | null;
metadata?: Record<string, any> | null;
};
export type ProductOptionValue = {
id: string;
value: string;
option_id: string;
option?: ProductOption | null;
variant_id: string;
variant?: ProductVariant | null;
created_at: string;
updated_at: string;
deleted_at?: string | null;
metadata?: Record<string, any> | null;
};
type ShippingOption = { type ShippingOption = {
id: string; id: string;
name: string; name: string;
@ -287,3 +309,8 @@ export type Cart = Partial<MedusaCart> & {
lines: []; lines: [];
totalQuantity: number; totalQuantity: number;
}; };
export type Menu = {
title: string;
path: string;
};