mirror of
https://github.com/vercel/commerce.git
synced 2025-06-19 21:51:21 +00:00
Currently for a user with a screen reader it would be near impossible to use this page. This change will read clearly what the swatch is and for what group it belongs.
165 lines
4.9 KiB
TypeScript
165 lines
4.9 KiB
TypeScript
import cn from 'classnames'
|
|
import Image from 'next/image'
|
|
import { NextSeo } from 'next-seo'
|
|
import { FC, useState } from 'react'
|
|
import s from './ProductView.module.css'
|
|
|
|
import { Swatch, ProductSlider } from '@components/product'
|
|
import { Button, Container, Text, useUI } from '@components/ui'
|
|
|
|
import type { Product } from '@commerce/types'
|
|
import usePrice from '@framework/product/use-price'
|
|
import { useAddItem } from '@framework/cart'
|
|
|
|
import { getVariant, SelectedOptions } from '../helpers'
|
|
import WishlistButton from '@components/wishlist/WishlistButton'
|
|
|
|
interface Props {
|
|
className?: string
|
|
children?: any
|
|
product: Product
|
|
}
|
|
|
|
const ProductView: FC<Props> = ({ product }) => {
|
|
const addItem = useAddItem()
|
|
const { price } = usePrice({
|
|
amount: product.price.value,
|
|
baseAmount: product.price.retailPrice,
|
|
currencyCode: product.price.currencyCode!,
|
|
})
|
|
const { openSidebar } = useUI()
|
|
const [loading, setLoading] = useState(false)
|
|
const [choices, setChoices] = useState<SelectedOptions>({
|
|
size: null,
|
|
color: null,
|
|
})
|
|
|
|
// Select the correct variant based on choices
|
|
const variant = getVariant(product, choices)
|
|
|
|
const addToCart = async () => {
|
|
setLoading(true)
|
|
try {
|
|
await addItem({
|
|
productId: String(product.id),
|
|
variantId: String(variant ? variant.id : product.variants[0].id),
|
|
})
|
|
openSidebar()
|
|
setLoading(false)
|
|
} catch (err) {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Container className="max-w-none w-full" clean>
|
|
<NextSeo
|
|
title={product.name}
|
|
description={product.description}
|
|
openGraph={{
|
|
type: 'website',
|
|
title: product.name,
|
|
description: product.description,
|
|
images: [
|
|
{
|
|
url: product.images[0]?.url!,
|
|
width: 800,
|
|
height: 600,
|
|
alt: product.name,
|
|
},
|
|
],
|
|
}}
|
|
/>
|
|
<div className={cn(s.root, 'fit')}>
|
|
<div className={cn(s.productDisplay, 'fit')}>
|
|
<div className={s.nameBox}>
|
|
<h1 className={s.name}>{product.name}</h1>
|
|
<div className={s.price}>
|
|
{price}
|
|
{` `}
|
|
{product.price?.currencyCode}
|
|
</div>
|
|
</div>
|
|
|
|
<div className={s.sliderContainer}>
|
|
<ProductSlider key={product.id}>
|
|
{product.images.map((image, i) => (
|
|
<div key={image.url} className={s.imageContainer}>
|
|
<Image
|
|
className={s.img}
|
|
src={image.url!}
|
|
alt={image.alt || 'Product Image'}
|
|
width={1050}
|
|
height={1050}
|
|
priority={i === 0}
|
|
quality="85"
|
|
/>
|
|
</div>
|
|
))}
|
|
</ProductSlider>
|
|
</div>
|
|
</div>
|
|
<div className={s.sidebar}>
|
|
<section>
|
|
{product.options?.map((opt) => (
|
|
<div className="pb-4" key={opt.displayName}>
|
|
<h2 className="uppercase font-medium">{opt.displayName}</h2>
|
|
<div role="listbox" className="flex flex-row py-4">
|
|
{opt.values.map((v, i: number) => {
|
|
const active = (choices as any)[
|
|
opt.displayName.toLowerCase()
|
|
]
|
|
|
|
return (
|
|
<Swatch
|
|
key={`${opt.id}-${i}`}
|
|
active={v.label.toLowerCase() === active}
|
|
variant={opt.displayName}
|
|
color={v.hexColors ? v.hexColors[0] : ''}
|
|
label={v.label}
|
|
onClick={() => {
|
|
setChoices((choices) => {
|
|
return {
|
|
...choices,
|
|
[opt.displayName.toLowerCase()]: v.label.toLowerCase(),
|
|
}
|
|
})
|
|
}}
|
|
/>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
))}
|
|
|
|
<div className="pb-14 break-words w-full max-w-xl">
|
|
<Text html={product.description} />
|
|
</div>
|
|
</section>
|
|
<div>
|
|
<Button
|
|
aria-label="Add to Cart"
|
|
type="button"
|
|
className={s.button}
|
|
onClick={addToCart}
|
|
loading={loading}
|
|
disabled={!variant && product.options.length > 0}
|
|
>
|
|
Add to Cart
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
{process.env.COMMERCE_WISHLIST_ENABLED && (
|
|
<WishlistButton
|
|
className={s.wishlistButton}
|
|
productId={product.id}
|
|
variant={product.variants[0]! as any}
|
|
/>
|
|
)}
|
|
</div>
|
|
</Container>
|
|
)
|
|
}
|
|
|
|
export default ProductView
|