Я хочу отображать сопутствующие товары для продукта, который не был опубликован, используя дескриптор, полученный по URL-адресу.
Я хочу отобразить в этом скелете компонент, который предоставляет связанный продукт для продукта, который не был опубликован, и теперь мне удалось получить дескриптор продукта.
Я попытался назвать здесь компонент, который приносит сопутствующие товары
const SkeletonProductPage = () => {
const [product, setProduct] = useState<Product | null>(null)
useEffect(() => {
const url = window.location.href
const urlParts = url.split("/")
const handleWithParams = urlParts[urlParts.length - 1]
const handle = handleWithParams.split("?")[0]
const productHandle = handle
const fetchProductByHandle = async (handle: string) => {
try {
// Fetch the product data from your backend API
const response = await fetch(`/products/${handle}`)
const data = await response.json()
// Set the product data in the state
setProduct(data)
} catch (error) {
console.error("Error fetching product data:", error)
}
}
fetchProductByHandle(handle)
}, [])
return (
<div>
<div className="content-container flex flex-col small:flex-row small:items-start py-6 relative">
<div className="flex flex-col gap-y-8 w-full">
<div className="flex items-start relative">
<div className="flex flex-col text-center flex-1 small:mx-16 gap-y-4 mt-10">
Product is no longer available
</div>
{console.log("proddd", product)}
<div className="my-16 px-8 small:px-8 content-container min-[1440px]:px-0">
{product ? (
<RelatedProducts product={product} classname={"w-full"} />
) : null}
</div>
</div>
</div>
</div>
</div>
)
}
export default SkeletonProductPage
Но все, что я могу до сих пор визуализировать, это просто текст о том, что продукт больше не доступен.
Это исходный код компонента, который содержит сопутствующие продукты.
type RelatedProductsProps = {
product: Product
classname?: any
}
const RelatedProducts = ({ product, classname }: RelatedProductsProps) => {
const { cart } = useCart()
const { t } = useTranslation()
const { client } = useMedusa()
const carouselRef = useRef<Slider>(null)
const [slidesToShow, setSlidesToShow] = useState(3)
const [slidesToScroll, setSlidesToScroll] = useState(3)
const handlePrev = () => {
carouselRef.current?.slickPrev()
}
const handleNext = () => {
carouselRef.current?.slickNext()
}
const settings = {
slidesToShow: 3,
slidesToScroll: 3,
dots: true,
infinite: true,
speed: 500,
adaptiveHeight: true,
adaptiveWidth: true,
responsive: [
{
breakpoint: 1024,
settings: {
slidesToShow: 3,
slidesToScroll: 3,
dots: true,
},
},
{
breakpoint: 780,
settings: {
slidesToShow: 2,
slidesToScroll: 2,
initialSlide: 2,
},
},
{
breakpoint: 680,
settings: {
slidesToShow: 1,
slidesToScroll: 1,
},
},
],
prevArrow: (
<button
className="slick-prev"
onClick={handlePrev}
style={{
position: "absolute",
top: "50%",
transform: "translateY(-50%)",
left: 0,
zIndex: 1,
backgroundColor: "#0000 !important",
color: "#0000 !important",
padding: "8px 16px",
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
transition: "background-color 0.2s",
}}
>
Prev
</button>
),
nextArrow: (
<button
className="slick-next"
onClick={handleNext}
style={{
position: "absolute",
top: "50%",
transform: "translateY(-50%)",
right: 0,
zIndex: 1,
backgroundColor: "#0000 !important",
color: "#0000 !important",
padding: "8px 16px",
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
transition: "background-color 0.2s",
}}
>
Next
</button>
),
}
const queryParams: StoreGetProductsParams = useMemo(() => {
const params: StoreGetProductsParams = {}
if (cart?.id) {
params.cart_id = cart.id
}
if (product.related_products && product.related_products.length > 0) {
params.id = product.related_products.map((p) => p.related_product_id)
return params
}
if (product.collection_id) {
params.collection_id = [product.collection_id]
}
if (product.type) {
params.type_id = [product.type.id]
}
if (product.tags) {
params.tags = product.tags.map((t) => t.value)
}
params.is_giftcard = false
return params
}, [product, cart?.id])
const { data, hasNextPage, fetchNextPage, isLoading, isFetchingNextPage } =
useInfiniteQuery(
[`infinite-products-${product.id}`, queryParams, cart],
({ pageParam }) =>
fetchProductsList({ medusaClient: client, pageParam, queryParams }),
{
getNextPageParam: (lastPage) => lastPage.nextPage,
}
)
const previews = usePreviews({ pages: data?.pages })
const translatedPreviews = useEntityTranslation(
previews
) as ProductPreviewType[]
useEffect(() => {
const handleResize = () => {
const width = window.innerWidth
let updatedSlidesToShow = 3
let updatedSlidesToScroll = 3
if (width >= 1024) {
updatedSlidesToShow = 3
updatedSlidesToScroll = 3
} else if (width >= 780) {
updatedSlidesToShow = 2
updatedSlidesToScroll = 2
} else {
updatedSlidesToShow = 1
updatedSlidesToScroll = 1
}
setSlidesToShow(updatedSlidesToShow)
setSlidesToScroll(updatedSlidesToScroll)
}
handleResize()
window.addEventListener("resize", handleResize)
return () => window.removeEventListener("resize", handleResize)
}, [])
return (
<div
className={classname ? classname : "max-w-2xl product-page-constraint"}
>
<div className="overflow-x-hidden p-[25px]">
<div className="flex flex-col items-center text-center mb-2">
<span
className={clsx(
"text-[13px] leading-[20px] mb-[8px] tracking-[2%] uppercase font-medium",
themeConfigs[THEME].secondaryFont,
themeConfigs[THEME].primaryTextColor
)}
>
{t("productDetails.common.relatedProducts")}
</span>
<p
className={clsx(
"max-w-lg",
THEME === "FASHION"
? "text-[40px] leading-[48px]"
: "text-[32px] leading-[40px] ",
themeConfigs[THEME].primaryFont,
themeConfigs[THEME].primaryTextColor
)}
>
{t("productDetails.common.relatedProductsSubtitle")}
</p>
</div>
{!isLoading && translatedPreviews.length === 0 ? (
<div
className={clsx(
"text-[16px] leading-[24px] my-8 tracking-[2%] text-center",
themeConfigs[THEME].secondaryFont
)}
>
{t("productDetails.common.noRelatedProducts")}
</div>
) : null}
<Slider ref={carouselRef} {...settings}>
{translatedPreviews.map((p, index) => (
<div
className="flex-none pr-5 w-fit-content"
data-cy={`related-product-${index}`}
key={p.id}
style={{
width: `fit-content`,
flex: "0 0 auto",
padding: "20px",
marginRight: "14px",
maxWidth: "100%",
}}
>
<ProductPreview {...p} />
</div>
))}
{isLoading &&
!translatedPreviews.length &&
repeat(8).map((index) => (
<div className="flex-none" key={index}>
<SkeletonProductPreview />
</div>
))}
{isFetchingNextPage &&
repeat(getNumberOfSkeletons(data?.pages)).map((index) => (
<div className="flex-none" key={index}>
<SkeletonProductPreview />
</div>
))}
</Slider>
{hasNextPage && (
<div
className={clsx(
"flex items-center justify-center mt-8",
themeConfigs[THEME].secondaryFont
)}
>
<Button
isLoading={isLoading}
onClick={() => fetchNextPage()}
className="w-72"
>
{t("productDetails.common.loadMore")}
</Button>
</div>
)}
</div>
</div>
)
}
export default RelatedProducts