From a522eda820c6c44572a0c494cf8dcdbd082b288b Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 11 Mar 2026 19:40:53 +0000 Subject: [PATCH] Switch to version 1: modified src/hooks/useProductDetail.ts --- src/hooks/useProductDetail.ts | 228 +++++++++++++++++++++++++++------- 1 file changed, 186 insertions(+), 42 deletions(-) diff --git a/src/hooks/useProductDetail.ts b/src/hooks/useProductDetail.ts index a528e68..dd0c4d7 100644 --- a/src/hooks/useProductDetail.ts +++ b/src/hooks/useProductDetail.ts @@ -1,52 +1,196 @@ -'use client'; +"use client"; -import { useState, useEffect } from 'react'; +import { useState, useMemo, useCallback } from "react"; +import { useProduct } from "./useProduct"; +import type { Product } from "@/lib/api/product"; +import type { ProductVariant } from "@/components/ecommerce/productDetail/ProductDetailCard"; +import type { ExtendedCartItem } from "./useCart"; -interface ProductDetail { - id: string; - name: string; - price: number; - description: string; +interface ProductImage { + src: string; + alt: string; } -interface UseProductDetailState { - product: ProductDetail | null; - loading: boolean; - error: string | null; +interface ProductMeta { + salePrice?: string; + ribbon?: string; + inventoryStatus?: string; + inventoryQuantity?: number; + sku?: string; } -export const useProductDetail = (productId: string) => { - const [state, setState] = useState({ - product: null, - loading: false, - error: null, - }); +export function useProductDetail(productId: string) { + const { product, isLoading, error } = useProduct(productId); + const [selectedQuantity, setSelectedQuantity] = useState(1); + const [selectedVariants, setSelectedVariants] = useState>({}); - useEffect(() => { - if (!productId) return; + const images = useMemo(() => { + if (!product) return []; - const loadProduct = async () => { - setState((prev) => ({ ...prev, loading: true })); - try { - // Mock data - const mockProduct: ProductDetail = { - id: productId, - name: 'Sample Product', - price: 99.99, - description: 'This is a sample product', + if (product.images && product.images.length > 0) { + return product.images.map((src, index) => ({ + src, + alt: product.imageAlt || `${product.name} - Image ${index + 1}`, + })); + } + return [{ + src: product.imageSrc, + alt: product.imageAlt || product.name, + }]; + }, [product]); + + const meta = useMemo(() => { + if (!product?.metadata) return {}; + + const metadata = product.metadata; + + let salePrice: string | undefined; + const onSaleValue = metadata.onSale; + const onSale = String(onSaleValue) === "true" || onSaleValue === 1 || String(onSaleValue) === "1"; + const salePriceValue = metadata.salePrice; + + if (onSale && salePriceValue !== undefined && salePriceValue !== null) { + if (typeof salePriceValue === 'number') { + salePrice = `$${salePriceValue.toFixed(2)}`; + } else { + const salePriceStr = String(salePriceValue); + salePrice = salePriceStr.startsWith('$') ? salePriceStr : `$${salePriceStr}`; + } + } + + let inventoryQuantity: number | undefined; + if (metadata.inventoryQuantity !== undefined) { + const qty = metadata.inventoryQuantity; + inventoryQuantity = typeof qty === 'number' ? qty : parseInt(String(qty), 10); + } + + return { + salePrice, + ribbon: metadata.ribbon ? String(metadata.ribbon) : undefined, + inventoryStatus: metadata.inventoryStatus ? String(metadata.inventoryStatus) : undefined, + inventoryQuantity, + sku: metadata.sku ? String(metadata.sku) : undefined, }; - setState({ product: mockProduct, loading: false, error: null }); - } catch (err) { - setState((prev) => ({ - ...prev, - loading: false, - error: 'Failed to load product', - })); - } + }, [product]); + + const variants = useMemo(() => { + if (!product) return []; + + const variantList: ProductVariant[] = []; + + if (product.metadata?.variantOptions) { + try { + const variantOptionsStr = String(product.metadata.variantOptions); + const parsedOptions = JSON.parse(variantOptionsStr); + + if (Array.isArray(parsedOptions)) { + parsedOptions.forEach((option: any) => { + if (option.name && option.values) { + const values = typeof option.values === 'string' + ? option.values.split(',').map((v: string) => v.trim()) + : Array.isArray(option.values) + ? option.values.map((v: any) => String(v).trim()) + : [String(option.values)]; + + if (values.length > 0) { + const optionLabel = option.name; + const currentSelected = selectedVariants[optionLabel] || values[0]; + + variantList.push({ + label: optionLabel, + options: values, + selected: currentSelected, + onChange: (value) => { + setSelectedVariants((prev) => ({ + ...prev, + [optionLabel]: value, + })); + }, + }); + } + } + }); + } + } catch (error) { + console.warn("Failed to parse variantOptions:", error); + } + } + + if (variantList.length === 0 && product.brand) { + variantList.push({ + label: "Brand", + options: [product.brand], + selected: product.brand, + onChange: () => { }, + }); + } + + if (variantList.length === 0 && product.variant) { + const variantOptions = product.variant.includes('/') + ? product.variant.split('/').map(v => v.trim()) + : [product.variant]; + + const variantLabel = "Variant"; + const currentSelected = selectedVariants[variantLabel] || variantOptions[0]; + + variantList.push({ + label: variantLabel, + options: variantOptions, + selected: currentSelected, + onChange: (value) => { + setSelectedVariants((prev) => ({ + ...prev, + [variantLabel]: value, + })); + }, + }); + } + + return variantList; + }, [product, selectedVariants]); + + const quantityVariant = useMemo(() => ({ + label: "Quantity", + options: Array.from({ length: 10 }, (_, i) => String(i + 1)), + selected: String(selectedQuantity), + onChange: (value) => setSelectedQuantity(parseInt(value, 10)), + }), [selectedQuantity]); + + const createCartItem = useCallback((): ExtendedCartItem | null => { + if (!product) return null; + + const variantStrings = Object.entries(selectedVariants).map( + ([label, value]) => `${label}: ${value}` + ); + + if (variantStrings.length === 0 && product.variant) { + variantStrings.push(`Variant: ${product.variant}`); + } + + const variantId = Object.values(selectedVariants).join('-') || 'default'; + + return { + id: `${product.id}-${variantId}-${selectedQuantity}`, + productId: product.id, + name: product.name, + variants: variantStrings, + price: product.price, + quantity: selectedQuantity, + imageSrc: product.imageSrc, + imageAlt: product.imageAlt || product.name, + }; + }, [product, selectedVariants, selectedQuantity]); + + return { + product, + isLoading, + error, + images, + meta, + variants, + quantityVariant, + selectedQuantity, + selectedVariants, + createCartItem, }; - - loadProduct(); - }, [productId]); - - return state; -}; +}