mirror of
https://github.com/vercel/commerce.git
synced 2025-06-28 09:21:22 +00:00
feat: change price by product variation
This commit is contained in:
parent
87dd5ef8e8
commit
e7be2b4695
@ -100,7 +100,7 @@ export default async function ProductPage(props: { params: Promise<{ name: strin
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
)}
|
)}
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<ProductDescription product={product} />
|
<ProductDescription product={product} variations={variations} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<AddToCart product={product} variations={variations}/>
|
<AddToCart product={product} variations={variations}/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,7 +4,6 @@ import { PlusIcon } from '@heroicons/react/24/outline';
|
|||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useProduct } from 'components/product/product-context';
|
import { useProduct } from 'components/product/product-context';
|
||||||
import { Product, ProductVariations } from 'lib/woocomerce/models/product';
|
import { Product, ProductVariations } from 'lib/woocomerce/models/product';
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { useCart } from './cart-context';
|
import { useCart } from './cart-context';
|
||||||
|
|
||||||
function SubmitButton({disabled = false}: {disabled: boolean}) {
|
function SubmitButton({disabled = false}: {disabled: boolean}) {
|
||||||
@ -23,21 +22,9 @@ function SubmitButton({disabled = false}: {disabled: boolean}) {
|
|||||||
|
|
||||||
export function AddToCart({ product, variations }: { product: Product, variations?: ProductVariations[] }) {
|
export function AddToCart({ product, variations }: { product: Product, variations?: ProductVariations[] }) {
|
||||||
const { setNewCart } = useCart();
|
const { setNewCart } = useCart();
|
||||||
const {state} = useProduct();
|
const { state } = useProduct();
|
||||||
|
const productVariant = variations?.find((variation) => variation.id.toString() === state.variation);
|
||||||
|
const variation = productVariant?.attributes.map((attr) => ({ attribute: attr.name, value: attr.option })) || [];
|
||||||
const productVariant = useMemo(() => {
|
|
||||||
const keys = Object.keys(state).filter((key) => key !== 'id' && key !== 'image').map((key) => ({
|
|
||||||
attribute: key.toLowerCase(),
|
|
||||||
value: state[key]
|
|
||||||
}));
|
|
||||||
const productExist = variations?.find((variation) => {
|
|
||||||
const attributes = variation.attributes.map((attr) => ({name: attr.name, option: attr.option})) || [];
|
|
||||||
return attributes.every((attribute) => attribute.option === keys.find((key) => key.attribute === attribute.name)?.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
return productExist ? keys : [];
|
|
||||||
}, [state, variations]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
@ -46,7 +33,7 @@ export function AddToCart({ product, variations }: { product: Product, variation
|
|||||||
const cart = await (
|
const cart = await (
|
||||||
await fetch('/api/cart', {
|
await fetch('/api/cart', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ id: product.id, quantity: 1, variation: productVariant })
|
body: JSON.stringify({ id: product.id, quantity: 1, variation })
|
||||||
})
|
})
|
||||||
).json();
|
).json();
|
||||||
setNewCart(cart);
|
setNewCart(cart);
|
||||||
@ -55,7 +42,7 @@ export function AddToCart({ product, variations }: { product: Product, variation
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SubmitButton disabled={variations?.length && !productVariant.length ? true : false}/>
|
<SubmitButton disabled={variations?.length && !product ? true : false}/>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,15 @@ type ProductState = {
|
|||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
} & {
|
} & {
|
||||||
image?: string;
|
image?: string;
|
||||||
};
|
} & {
|
||||||
|
variation?: string;
|
||||||
|
}
|
||||||
|
|
||||||
type ProductContextType = {
|
type ProductContextType = {
|
||||||
state: ProductState;
|
state: ProductState;
|
||||||
updateOption: (name: string, value: string) => ProductState;
|
updateOption: (name: string, value: string) => ProductState;
|
||||||
updateImage: (index: string) => ProductState;
|
updateImage: (index: string) => ProductState;
|
||||||
|
updateVariation: (variation: string) => ProductState;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ProductContext = createContext<ProductContextType | undefined>(undefined);
|
const ProductContext = createContext<ProductContextType | undefined>(undefined);
|
||||||
@ -48,11 +51,18 @@ export function ProductProvider({ children }: { children: React.ReactNode }) {
|
|||||||
return { ...state, ...newState };
|
return { ...state, ...newState };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateVariation = (variation: string) => {
|
||||||
|
const newState = { variation };
|
||||||
|
setOptimisticState(newState);
|
||||||
|
return { ...state, ...newState };
|
||||||
|
}
|
||||||
|
|
||||||
const value = useMemo(
|
const value = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
state,
|
state,
|
||||||
updateOption,
|
updateOption,
|
||||||
updateImage,
|
updateImage,
|
||||||
|
updateVariation
|
||||||
}),
|
}),
|
||||||
[state]
|
[state]
|
||||||
);
|
);
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
|
'use client';
|
||||||
import Price from 'components/price';
|
import Price from 'components/price';
|
||||||
import Prose from 'components/prose';
|
import Prose from 'components/prose';
|
||||||
import { Product } from 'lib/woocomerce/models/product';
|
import { Product, ProductVariations } from 'lib/woocomerce/models/product';
|
||||||
|
import { useProduct } from './product-context';
|
||||||
|
|
||||||
|
export function ProductDescription({ product, variations }: { product: Product, variations?: ProductVariations[] }) {
|
||||||
|
const { state } = useProduct();
|
||||||
|
const productVariant = variations?.find((variation) => variation.id.toString() === state.variation);
|
||||||
|
|
||||||
export function ProductDescription({ product }: { product: Product }) {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mb-6 flex flex-col border-b pb-6 dark:border-neutral-700">
|
<div className="mb-6 flex flex-col border-b pb-6 dark:border-neutral-700">
|
||||||
<h1 className="mb-2 text-5xl font-medium">{product.name}</h1>
|
<h1 className="mb-2 text-5xl font-medium">{product.name}</h1>
|
||||||
<div className="mr-auto w-auto rounded-full bg-blue-600 p-2 text-sm text-white">
|
<div className="mr-auto w-auto rounded-full bg-blue-600 p-2 text-sm text-white">
|
||||||
<Price amount={product.price} currencyCode="EUR" />
|
<Price amount={productVariant ? productVariant.price : product.price} currencyCode="EUR" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{product.description ? (
|
{product.description ? (
|
||||||
|
@ -35,12 +35,19 @@ export function VariantSelector({
|
|||||||
const optionNameLowerCase = option?.name?.toLowerCase();
|
const optionNameLowerCase = option?.name?.toLowerCase();
|
||||||
// The option is active if it's in the selected options.
|
// The option is active if it's in the selected options.
|
||||||
const isActive = optionNameLowerCase ? state[optionNameLowerCase] === value : false;
|
const isActive = optionNameLowerCase ? state[optionNameLowerCase] === value : false;
|
||||||
if (!optionNameLowerCase) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
formAction={() => {
|
formAction={() => {
|
||||||
const newState = updateOption(optionNameLowerCase, value);
|
if (!optionNameLowerCase) return;
|
||||||
|
let newState = updateOption(optionNameLowerCase, value);
|
||||||
|
const keys = Object.keys(newState).filter((key) => key !== 'id' && key !== 'image' && key !== 'variation');
|
||||||
|
const variant = variations.find((variation) => {
|
||||||
|
return variation?.attributes?.every((attr) => attr.name && keys.includes(attr.name) && newState[attr.name] === attr.option);
|
||||||
|
})?.id?.toString();
|
||||||
|
if (variant) {
|
||||||
|
newState = {...newState, variation: variant};
|
||||||
|
}
|
||||||
updateURL(newState);
|
updateURL(newState);
|
||||||
}}
|
}}
|
||||||
key={value}
|
key={value}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user