mirror of
https://github.com/vercel/commerce.git
synced 2025-05-20 08:26:59 +00:00
feat: product options and variants
This commit is contained in:
parent
bff927e700
commit
e0e67f4270
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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({
|
||||||
|
@ -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;
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user