mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 20:26:49 +00:00
fix: update core charge appearance
Signed-off-by: Chloe <pinkcloudvnn@gmail.com>
This commit is contained in:
103
components/product/core-charge.tsx
Normal file
103
components/product/core-charge.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
'use client';
|
||||
|
||||
import Price from 'components/price';
|
||||
import { CORE_VARIANT_ID_KEY, CORE_WAIVER } from 'lib/constants';
|
||||
import { Money, ProductVariant } from 'lib/shopify/types';
|
||||
import { cn, createUrl } from 'lib/utils';
|
||||
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
||||
|
||||
type CoreChargeProps = {
|
||||
variants: ProductVariant[];
|
||||
defaultPrice: Money;
|
||||
};
|
||||
const CoreCharge = ({ variants, defaultPrice }: CoreChargeProps) => {
|
||||
const searchParams = useSearchParams();
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
|
||||
const optionSearchParams = new URLSearchParams(searchParams);
|
||||
const coreVariantIdSearchParam = optionSearchParams.get(CORE_VARIANT_ID_KEY);
|
||||
|
||||
const variant = variants.find((variant: ProductVariant) =>
|
||||
variant.selectedOptions.every(
|
||||
(option) => option.value === optionSearchParams.get(option.name.toLowerCase())
|
||||
)
|
||||
);
|
||||
|
||||
const { coreCharge, waiverAvailable } = variant ?? {};
|
||||
|
||||
const handleSelectCoreChargeOption = (action: 'add' | 'remove') => {
|
||||
if (action === 'add' && variant?.coreVariantId) {
|
||||
optionSearchParams.set(CORE_VARIANT_ID_KEY, variant.coreVariantId);
|
||||
} else if (action === 'remove') {
|
||||
optionSearchParams.set(CORE_VARIANT_ID_KEY, CORE_WAIVER);
|
||||
}
|
||||
|
||||
const newUrl = createUrl(pathname, optionSearchParams);
|
||||
router.replace(newUrl, { scroll: false });
|
||||
};
|
||||
|
||||
// if the selected variant has changed, and the core change variant id is not the same as the selected variant id
|
||||
// or if users have selected the core waiver but the selected variant does not have a waiver available
|
||||
// we remove the core charge from the url
|
||||
if (
|
||||
variant?.coreVariantId &&
|
||||
optionSearchParams.has(CORE_VARIANT_ID_KEY) &&
|
||||
(coreVariantIdSearchParam !== CORE_WAIVER || !variant.waiverAvailable) &&
|
||||
coreVariantIdSearchParam !== variant.coreVariantId
|
||||
) {
|
||||
optionSearchParams.delete(CORE_VARIANT_ID_KEY);
|
||||
const newUrl = createUrl(pathname, optionSearchParams);
|
||||
router.replace(newUrl, { scroll: false });
|
||||
}
|
||||
|
||||
const selectedPayCoreCharge = coreVariantIdSearchParam === variant?.coreVariantId;
|
||||
const selectedCoreWaiver = coreVariantIdSearchParam === CORE_WAIVER;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col text-xs lg:text-sm">
|
||||
<div className="mb-2 text-base font-medium">Core Charge</div>
|
||||
<p className="mb-2 text-sm tracking-tight text-neutral-500">
|
||||
The core charge is a refundable deposit that is added to the price of the part. This charge
|
||||
ensures that the old, worn-out part is returned to the supplier for proper disposal or
|
||||
recycling. When you return the old part, you'll receive a refund of the core charge.
|
||||
</p>
|
||||
<ul className="flex min-h-16 flex-row space-x-4 pt-2">
|
||||
{waiverAvailable ? (
|
||||
<li className="flex w-32">
|
||||
<button
|
||||
onClick={() => handleSelectCoreChargeOption('remove')}
|
||||
className={cn(
|
||||
'flex w-full flex-col flex-wrap items-center justify-center space-y-2 rounded-md border p-2 text-center text-xs font-medium',
|
||||
{
|
||||
'ring-2 ring-secondary': selectedCoreWaiver
|
||||
}
|
||||
)}
|
||||
>
|
||||
<span>Core Waiver</span>
|
||||
<Price amount="0" currencyCode={defaultPrice.currencyCode} />
|
||||
</button>
|
||||
</li>
|
||||
) : null}
|
||||
{coreCharge && variant?.coreVariantId ? (
|
||||
<li className="flex w-32">
|
||||
<button
|
||||
onClick={() => handleSelectCoreChargeOption('add')}
|
||||
className={cn(
|
||||
'flex w-full flex-col flex-wrap items-center justify-center space-y-2 rounded-md border p-2 text-center text-xs font-medium',
|
||||
{
|
||||
'ring-2 ring-secondary': selectedPayCoreCharge
|
||||
}
|
||||
)}
|
||||
>
|
||||
<span>Core Charge</span>
|
||||
<Price amount={coreCharge.amount} currencyCode={coreCharge.currencyCode} />
|
||||
</button>
|
||||
</li>
|
||||
) : null}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CoreCharge;
|
@@ -1,45 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import CoreCharge from 'components/core-charge';
|
||||
import Price from 'components/price';
|
||||
import { Money, ProductVariant } from 'lib/shopify/types';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
type PriceWithCoreChargeProps = {
|
||||
variants: ProductVariant[];
|
||||
defaultPrice: Money;
|
||||
};
|
||||
|
||||
const PriceWithCoreCharge = ({ variants, defaultPrice }: PriceWithCoreChargeProps) => {
|
||||
const searchParams = useSearchParams();
|
||||
const variant = variants.find((variant: ProductVariant) =>
|
||||
variant.selectedOptions.every(
|
||||
(option) => option.value === searchParams.get(option.name.toLowerCase())
|
||||
)
|
||||
);
|
||||
|
||||
const price = variant?.price.amount || defaultPrice.amount;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-4">
|
||||
{variant && (
|
||||
<div className="flex flex-row items-center space-x-3 text-sm text-neutral-700">
|
||||
{variant.sku && <span>SKU: {variant.sku}</span>}
|
||||
{variant.barcode && <span>Part Number: {variant.barcode}</span>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mr-auto flex w-auto flex-row flex-wrap items-center gap-3 text-sm">
|
||||
<Price
|
||||
amount={price}
|
||||
currencyCode={variant?.price.currencyCode || defaultPrice.currencyCode}
|
||||
className="text-2xl font-semibold"
|
||||
/>
|
||||
<CoreCharge variant={variant} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PriceWithCoreCharge;
|
@@ -2,8 +2,9 @@ import { AddToCart } from 'components/cart/add-to-cart';
|
||||
import Prose from 'components/prose';
|
||||
import { Product } from 'lib/shopify/types';
|
||||
import { Suspense } from 'react';
|
||||
import PriceWithCoreCharge from './price-with-core-charge';
|
||||
import CoreCharge from './core-charge';
|
||||
import SpecialOffer from './special-offer';
|
||||
import VariantPrice from './vairant-price';
|
||||
import { VariantSelector } from './variant-selector';
|
||||
import Warranty from './warranty';
|
||||
|
||||
@@ -12,7 +13,7 @@ export function ProductDescription({ product }: { product: Product }) {
|
||||
<>
|
||||
<div className="mb-5 flex flex-col dark:border-neutral-700">
|
||||
<h1 className="mb-3 text-2xl font-bold">{product.title}</h1>
|
||||
<PriceWithCoreCharge
|
||||
<VariantPrice
|
||||
variants={product.variants}
|
||||
defaultPrice={product.priceRange.minVariantPrice}
|
||||
/>
|
||||
@@ -28,6 +29,10 @@ export function ProductDescription({ product }: { product: Product }) {
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<div className="mb-4 border-t pb-4 pt-6 dark:border-neutral-700">
|
||||
<CoreCharge variants={product.variants} defaultPrice={product.priceRange.minVariantPrice} />
|
||||
</div>
|
||||
|
||||
<div className="mb-4 border-t py-6 dark:border-neutral-700">
|
||||
<Warranty productType={product.productType} />
|
||||
</div>
|
||||
|
31
components/product/vairant-price.tsx
Normal file
31
components/product/vairant-price.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
'use client';
|
||||
|
||||
import Price from 'components/price';
|
||||
import { Money, ProductVariant } from 'lib/shopify/types';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
type PriceWithCoreChargeProps = {
|
||||
variants: ProductVariant[];
|
||||
defaultPrice: Money;
|
||||
};
|
||||
|
||||
const VariantPrice = ({ variants, defaultPrice }: PriceWithCoreChargeProps) => {
|
||||
const searchParams = useSearchParams();
|
||||
const variant = variants.find((variant: ProductVariant) =>
|
||||
variant.selectedOptions.every(
|
||||
(option) => option.value === searchParams.get(option.name.toLowerCase())
|
||||
)
|
||||
);
|
||||
|
||||
const price = variant?.price.amount || defaultPrice.amount;
|
||||
|
||||
return (
|
||||
<Price
|
||||
amount={price}
|
||||
currencyCode={variant?.price.currencyCode || defaultPrice.currencyCode}
|
||||
className="text-2xl font-semibold"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default VariantPrice;
|
@@ -51,6 +51,7 @@ export function VariantSelector({
|
||||
// Update the option params using the current option to reflect how the url *would* change,
|
||||
// if the option was clicked.
|
||||
optionSearchParams.set(optionNameLowerCase, value);
|
||||
|
||||
const optionUrl = createUrl(pathname, optionSearchParams);
|
||||
|
||||
// In order to determine if an option is available for sale, we need to:
|
||||
|
@@ -38,16 +38,19 @@ const WarrantySelector = () => {
|
||||
return (
|
||||
<ul className="flex min-h-16 flex-row space-x-4 pt-2">
|
||||
{plans.map((plan) => (
|
||||
<li
|
||||
key={plan.key}
|
||||
onClick={() => setSelectedOptions(plan.key)}
|
||||
className={cn(
|
||||
'flex w-32 cursor-pointer flex-col items-center justify-center space-y-2 rounded-md border p-2 text-center text-xs font-medium',
|
||||
{ 'ring-2 ring-secondary': plan.key === selectedOptions }
|
||||
)}
|
||||
>
|
||||
{plan.template}
|
||||
<Price amount={String(plan.price)} currencyCode="USD" />
|
||||
<li key={plan.key} className="flex w-32">
|
||||
<button
|
||||
onClick={() => setSelectedOptions(plan.key)}
|
||||
className={cn(
|
||||
'flex w-full flex-col flex-wrap items-center justify-center space-y-2 rounded-md border p-2 text-center text-xs font-medium',
|
||||
{
|
||||
'ring-2 ring-secondary': plan.key === selectedOptions
|
||||
}
|
||||
)}
|
||||
>
|
||||
{plan.template}
|
||||
<Price amount={String(plan.price)} currencyCode="USD" />
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
Reference in New Issue
Block a user