{
  "name": "product-variant-03-block",
  "type": "registry:block",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "separator",
    "https://ui.stackzero.co/r/image-carousel-basic.json",
    "https://ui.stackzero.co/r/price-format-basic.json",
    "https://ui.stackzero.co/r/variant-selector-basic.json",
    "https://ui.stackzero.co/r/quantity-input-basic.json"
  ],
  "files": [
    {
      "type": "registry:block",
      "content": "\"use client\";\n\nimport ImageCarousel_Basic, {\n  CarouselImage,\n} from \"@/components/commerce-ui/image-carousel-basic\";\nimport PriceFormat from \"@/components/commerce-ui/price-format-basic\";\nimport QuantityInputBasic from \"@/components/commerce-ui/quantity-input-basic\";\nimport VariantSelectorBasic, {\n  VariantItem as BaseVariantItem,\n} from \"@/components/commerce-ui/variant-selector-basic\";\nimport { Button } from \"@/components/ui/button\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { Check, ShieldCheck } from \"lucide-react\";\nimport { useState } from \"react\";\n\ninterface VariantItem extends BaseVariantItem {\n  price: number;\n  salePrice?: number;\n  images?: CarouselImage[];\n  isInStock?: boolean;\n  availableQuantity?: number | null;\n  features?: string[];\n}\n\ninterface VariantSelectionPayload {\n  variantId: string;\n  variantLabel: string;\n  quantity: number;\n  price: number;\n  salePrice?: number;\n  totalPrice: number;\n  isOnSale: boolean;\n}\n\ninterface ProductVariant03Props {\n  title?: string;\n  description?: string;\n  badge?: string | null;\n  variants: VariantItem[];\n  defaultImages?: CarouselImage[];\n  initialVariant?: string;\n  variantLabel?: string;\n  onAddToCart?: (payload: VariantSelectionPayload) => void;\n  onBuyNow?: (payload: VariantSelectionPayload) => void;\n  selectedVariant?: string;\n  onVariantChange?: (variant: string) => void;\n  quantity?: number;\n  onQuantityChange?: (quantity: number) => void;\n  isLoading?: boolean;\n  errorMessage?: string | null;\n  currencyPrefix?: string;\n  releaseDate?: string;\n}\n\nfunction ProductVariant_03({\n  badge = \"Just Released\",\n  currencyPrefix = \"$\",\n  defaultImages = [],\n  description = \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\",\n  errorMessage = null,\n  initialVariant,\n  isLoading = false,\n  onAddToCart = () => {},\n  onBuyNow = () => {},\n  onQuantityChange,\n  onVariantChange,\n  quantity: controlledQuantity,\n  releaseDate = \"August 15, 2023\",\n  selectedVariant: controlledVariant,\n  title = \"Product Variant Title\",\n  variantLabel = \"Model\",\n  variants = [],\n}: ProductVariant03Props) {\n  // Ensure variants array is not empty\n  if (!variants.length) {\n    throw new Error(\"At least one variant must be provided\");\n  }\n\n  const defaultInitialVariant = initialVariant || variants[0].value;\n\n  const [internalSelectedVariant, setInternalSelectedVariant] = useState(\n    defaultInitialVariant\n  );\n  const [internalQuantity, setInternalQuantity] = useState(1);\n\n  // Determine if we're in controlled or uncontrolled mode\n  const isVariantControlled = controlledVariant !== undefined;\n  const isQuantityControlled = controlledQuantity !== undefined;\n  const selectedVariantId = isVariantControlled\n    ? controlledVariant\n    : internalSelectedVariant;\n  const quantity = isQuantityControlled ? controlledQuantity : internalQuantity;\n\n  const handleVariantChange = (newVariant: string) => {\n    if (isVariantControlled) {\n      onVariantChange?.(newVariant);\n    } else {\n      setInternalSelectedVariant(newVariant);\n\n      // Reset quantity if changing to a variant with less available quantity\n      const newSelectedVariant = variants.find((v) => v.value === newVariant);\n      if (\n        newSelectedVariant?.isInStock &&\n        newSelectedVariant.availableQuantity !== null &&\n        newSelectedVariant.availableQuantity !== undefined &&\n        quantity > newSelectedVariant.availableQuantity\n      ) {\n        handleQuantityChange(newSelectedVariant.availableQuantity);\n      }\n    }\n  };\n\n  const handleQuantityChange = (newQuantity: number) => {\n    if (isQuantityControlled) {\n      onQuantityChange?.(newQuantity);\n    } else {\n      setInternalQuantity(newQuantity);\n    }\n  };\n\n  const selectedVariant =\n    variants.find((v) => v.value === selectedVariantId) || variants[0];\n\n  const currentImages = selectedVariant?.images || defaultImages;\n  const currentPrice = selectedVariant.price;\n  const currentSalePrice = selectedVariant.salePrice;\n  const isOnSale =\n    currentSalePrice !== undefined && currentSalePrice < currentPrice;\n  const effectivePrice = isOnSale ? currentSalePrice : currentPrice;\n\n  // Get stock status from the selected variant\n  const isInStock =\n    selectedVariant.isInStock !== undefined ? selectedVariant.isInStock : true;\n  const availableStock = selectedVariant.availableQuantity;\n  const isLowStock =\n    isInStock &&\n    availableStock !== null &&\n    availableStock !== undefined &&\n    availableStock <= 25;\n\n  // Get features for the selected variant\n  const features = selectedVariant.features || [];\n\n  const handleAddToCart = () => {\n    onAddToCart({\n      isOnSale,\n      price: currentPrice,\n      quantity,\n      salePrice: isOnSale ? currentSalePrice : undefined,\n      totalPrice: quantity * effectivePrice,\n      variantId: selectedVariantId,\n      variantLabel: selectedVariant?.label || \"\",\n    });\n  };\n\n  const handleBuyNow = () => {\n    onBuyNow({\n      isOnSale,\n      price: currentPrice,\n      quantity,\n      salePrice: isOnSale ? currentSalePrice : undefined,\n      totalPrice: quantity * effectivePrice,\n      variantId: selectedVariantId,\n      variantLabel: selectedVariant?.label || \"\",\n    });\n  };\n\n  if (errorMessage) {\n    return (\n      <div className=\"my-6 rounded-lg border border-red-200 bg-red-50 p-6 text-red-600 dark:border-red-900 dark:bg-red-900/20 dark:text-red-400\">\n        <p className=\"font-medium\">Error loading product</p>\n        <p className=\"text-sm\">{errorMessage}</p>\n      </div>\n    );\n  }\n\n  // Add visual indicator for out of stock items in variant selector\n  const variantsWithStockIndicator = variants.map((variant) => {\n    const isVariantInStock =\n      variant.isInStock !== undefined ? variant.isInStock : true;\n    return {\n      ...variant,\n      disabled: !isVariantInStock,\n      label: variant.label + (isVariantInStock ? \"\" : \" (Out of Stock)\"),\n    };\n  });\n\n  return (\n    <div className=\"flex max-w-screen-xl flex-col overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm transition-all duration-300 hover:shadow-md lg:flex-row dark:border-gray-800 dark:bg-gray-900\">\n      <div className=\"relative flex w-full flex-col bg-gradient-to-br from-sky-50 via-blue-50 to-indigo-50 p-6 lg:w-2/5 dark:from-gray-900 dark:via-blue-950/20 dark:to-gray-900\">\n        {badge && (\n          <div className=\"absolute top-3 left-3 z-10 flex items-center justify-center\">\n            <div className=\"animate-pulse-slow rounded-full bg-blue-600 px-3 py-1 text-xs font-bold tracking-wider text-white uppercase shadow-md\">\n              {badge}\n            </div>\n          </div>\n        )}\n\n        {/* Highlight glow effect */}\n        <div className=\"absolute inset-0\">\n          <div className=\"absolute -top-10 -left-10 h-32 w-32 rounded-full bg-blue-300/30 blur-2xl\"></div>\n          <div className=\"absolute right-0 -bottom-8 h-28 w-28 rounded-full bg-indigo-400/20 blur-xl\"></div>\n        </div>\n\n        <div className=\"z-10 mt-4 mb-auto flex w-full justify-center\">\n          {isLoading ? (\n            <div className=\"flex h-[300px] items-center justify-center\">\n              <div className=\"h-10 w-10 animate-spin rounded-full border-4 border-blue-200 border-t-blue-600\"></div>\n            </div>\n          ) : (\n            <ImageCarousel_Basic\n              images={currentImages}\n              aspectRatio=\"square\"\n              imageFit=\"contain\"\n              showThumbs={currentImages.length > 1}\n              thumbPosition=\"bottom\"\n              className=\"mx-auto max-w-full\"\n            />\n          )}\n        </div>\n      </div>\n\n      <div className=\"flex flex-1 flex-col p-6\">\n        <div className=\"mb-4\">\n          <div className=\"flex flex-col justify-between space-y-2 sm:flex-row sm:items-center sm:space-y-0\">\n            <h2 className=\"text-2xl font-bold text-gray-900 dark:text-white\">\n              {title}\n            </h2>\n            <PriceFormat\n              prefix={currencyPrefix}\n              value={effectivePrice}\n              className=\"text-2xl font-bold text-blue-600 dark:text-blue-400\"\n            />\n          </div>\n\n          <p className=\"mt-2 text-gray-600 dark:text-gray-400\">{description}</p>\n        </div>\n\n        <div className=\"grid grid-cols-1 gap-6 md:grid-cols-2\">\n          {/* Left column: Variants and quantity */}\n          <div className=\"flex flex-col space-y-4\">\n            <div>\n              <label className=\"mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300\">\n                {variantLabel}\n              </label>\n              <VariantSelectorBasic\n                value={selectedVariantId}\n                onValueChange={handleVariantChange}\n                variants={variantsWithStockIndicator}\n                itemClassName=\"bg-gray-50 border-gray-200 hover:border-blue-300 dark:bg-gray-800 dark:border-gray-700\n                            data-[state=checked]:border-blue-500 data-[state=checked]:bg-blue-50 \n                            data-[state=checked]:text-blue-700 dark:data-[state=checked]:bg-gray-700 \n                            dark:data-[state=checked]:border-blue-500 dark:data-[state=checked]:text-blue-300\n                            focus:ring-2 focus:ring-blue-500/50 focus:ring-offset-2 focus:border-blue-400\n                            dark:focus:ring-blue-500/40 dark:focus:ring-offset-gray-900\"\n              />\n            </div>\n\n            <div>\n              <label className=\"mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300\">\n                Quantity\n              </label>\n              <QuantityInputBasic\n                quantity={quantity}\n                onChange={handleQuantityChange}\n                max={\n                  availableStock !== null && availableStock !== undefined\n                    ? availableStock\n                    : 10\n                }\n                className=\"max-w-[150px] border-gray-300 dark:border-gray-700\"\n                disabled={!isInStock}\n              />\n            </div>\n          </div>\n          <div className=\"space-y-4\">\n            <h3 className=\"text-lg font-medium text-gray-900 dark:text-white\">\n              Features\n            </h3>\n            <ul className=\"grid grid-cols-1 gap-x-4 gap-y-2\">\n              {features.map((feature, index) => (\n                <li key={index} className=\"flex items-center\">\n                  <Check className=\"mr-2 h-5 w-5 text-blue-500\" />\n                  <span className=\"text-gray-700 dark:text-gray-300\">\n                    {feature}\n                  </span>\n                </li>\n              ))}\n            </ul>\n          </div>\n        </div>\n        <Separator className=\"my-6\" />\n        <div className=\"flex flex-col justify-between space-y-4 sm:flex-row sm:items-center sm:space-y-0\">\n          <div>\n            {!isInStock ? (\n              <p className=\"flex items-center text-sm font-medium text-red-600 dark:text-red-400\">\n                <span className=\"mr-2 inline-block h-2 w-2 rounded-full bg-red-500\"></span>\n                Out of Stock\n              </p>\n            ) : isLowStock ? (\n              <p className=\"flex items-center text-sm font-medium text-amber-600 dark:text-amber-400\">\n                <span className=\"mr-2 inline-block h-2 w-2 animate-pulse rounded-full bg-amber-500\"></span>\n                Only {availableStock} units left\n              </p>\n            ) : (\n              <p className=\"flex items-center text-sm font-medium text-green-600 dark:text-green-400\">\n                <span className=\"mr-2 inline-block h-2 w-2 rounded-full bg-green-500\"></span>\n                In Stock\n              </p>\n            )}\n            <p className=\"text-sm text-gray-500 dark:text-gray-400\">\n              Released on {releaseDate}\n            </p>\n            <p className=\"mt-2 text-sm font-medium\">\n              Total: {currencyPrefix}\n              {(quantity * effectivePrice).toFixed(2)}\n            </p>\n          </div>\n\n          <div className=\"flex space-x-3\">\n            <Button\n              variant=\"outline\"\n              onClick={handleAddToCart}\n              className=\"border-gray-300 bg-white text-gray-700 hover:border-blue-500 hover:bg-blue-50 hover:text-blue-600 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200\"\n              disabled={!isInStock || isLoading}\n            >\n              {isLoading ? \"Loading...\" : \"Add to cart\"}\n            </Button>\n            <Button\n              onClick={handleBuyNow}\n              className=\"bg-blue-600 text-white hover:bg-blue-700\"\n              disabled={!isInStock || isLoading}\n            >\n              {isLoading ? \"Loading...\" : \"Buy now\"}\n            </Button>\n          </div>\n        </div>\n\n        <div className=\"mt-4 rounded-md bg-blue-50/50 p-4 dark:bg-blue-950/20\">\n          <div className=\"flex items-center\">\n            <ShieldCheck className=\"mr-2 h-5 w-5 text-blue-500\" />\n            <span className=\"text-sm font-medium text-gray-700 dark:text-gray-300\">\n              Free 2-day shipping & 30-day money-back guarantee\n            </span>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default ProductVariant_03;\nexport type { ProductVariant03Props, VariantItem, VariantSelectionPayload };\n",
      "path": "/components/commerce-ui/blocks/product-variants/product-variants-03.tsx",
      "target": "components/commerce-ui/product-variants-03.tsx"
    }
  ]
}