{
  "name": "product-customizer",
  "title": "ProductCustomizer",
  "description": "Full product configuration with variants, add-ons, and add-to-cart.",
  "type": "component",
  "registryDependencies": [
    "price",
    "quantity-selector",
    "variant-selector",
    "add-on-selector",
    "composite-selector",
    "bundle-selector"
  ],
  "files": [
    {
      "path": "product-customizer.tsx",
      "content": "\"use client\";\n\nimport React, { useState, useCallback, useMemo, useEffect, useRef } from \"react\";\nimport { Button } from \"@base-ui/react/button\";\nimport type {\n  ProductWithDetails,\n  VariantView,\n  AddOnOption,\n  ComponentSelectionInput,\n  CompositePriceResult,\n} from \"@cimplify/sdk\";\nimport type { ProductBillingPlan } from \"@cimplify/sdk\";\nimport type { BundleSelectionInput } from \"@cimplify/sdk\";\nimport {\n  getProductCurrency,\n  getUnitPriceAtQuantity,\n  getVariantUnitPrice,\n  parsePrice,\n} from \"@cimplify/sdk\";\nimport { isMultiDayService, serviceBillingMultiplier } from \"../utils/price-basis\";\nimport { formatDuration } from \"./utils/format-duration\";\nimport { useCart, useQuote } from \"@cimplify/sdk/react\";\nimport type { AddToCartOptions } from \"@cimplify/sdk/react\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { QuantitySelector } from \"@cimplify/sdk/react\";\nimport { VariantSelector } from \"@cimplify/sdk/react\";\nimport { AddOnSelector } from \"@cimplify/sdk/react\";\nimport { StaffPicker } from \"@cimplify/sdk/react\";\nimport { BookingSummary } from \"./booking-summary\";\nimport type { Staff } from \"@cimplify/sdk\";\nimport { CompositeSelector } from \"@cimplify/sdk/react\";\nimport { BundleSelector } from \"@cimplify/sdk/react\";\nimport { BillingPlanSelector } from \"@cimplify/sdk/react\";\nimport { VolumePricing } from \"@cimplify/sdk/react\";\nimport { CustomerInputFields } from \"./customer-input-fields\";\nimport { DateSlotPicker } from \"@cimplify/sdk/react\";\nimport type { AvailableSlot } from \"@cimplify/sdk\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface ProductCustomizerClassNames {\n  root?: string;\n  actions?: string;\n  submitButton?: string;\n  submitButtonAdded?: string;\n  validation?: string;\n  specialInstructions?: string;\n  depositInfo?: string;\n  units?: string;\n  allergens?: string;\n  duration?: string;\n}\n\nexport interface ProductCustomizerProps {\n  product: ProductWithDetails;\n  onAddToCart?: (\n    product: ProductWithDetails,\n    quantity: number,\n    options: AddToCartOptions,\n  ) => void | Promise<void>;\n  /** Lets the parent swap its gallery for `variant.images` on selection. */\n  onVariantChange?: (variantId: string | undefined, variant: VariantView | undefined) => void;\n  showSpecialInstructions?: boolean;\n  /** Submit button verb; the running total is appended. Defaults to \"Add to Cart\". */\n  submitLabel?: string;\n  className?: string;\n  classNames?: ProductCustomizerClassNames;\n}\n\ninterface VariantSelection {\n  id?: string;\n  variant?: VariantView;\n}\n\nconst REQUIRED_OPTIONS_MESSAGE = \"Please select all required options\";\nconst QUOTE_LOADING_MESSAGE = \"Updating price…\";\nconst QUOTE_UNAVAILABLE_MESSAGE = \"Price is unavailable. Please try again.\";\nconst CUSTOMIZER_VALIDATION_ID = \"cimplify-customizer-validation\";\n\nfunction initialVariantSelection(product: ProductWithDetails): VariantSelection {\n  const variant =\n    product.variants?.find((candidate) => candidate.is_default) ?? product.variants?.[0];\n  return { id: variant?.id, variant };\n}\n\nexport function ProductCustomizer(props: ProductCustomizerProps): React.ReactElement {\n  // A product change represents a new customization session. Keying the\n  // stateful implementation resets it synchronously and avoids an effect that\n  // briefly renders the previous product's variant and quote.\n  return <ProductCustomizerContent key={props.product.id} {...props} />;\n}\n\nfunction ProductCustomizerContent({\n  product,\n  onAddToCart,\n  onVariantChange,\n  showSpecialInstructions = true,\n  submitLabel,\n  className,\n  classNames,\n}: ProductCustomizerProps): React.ReactElement {\n  const [quantity, setQuantity] = useState(product.min_order_quantity ?? 1);\n  const [isAdded, setIsAdded] = useState(false);\n  const [isSubmitting, setIsSubmitting] = useState(false);\n  const [variantSelection, setVariantSelection] = useState<VariantSelection>(() =>\n    initialVariantSelection(product),\n  );\n  const selectedVariantId = variantSelection.id;\n  const selectedVariant = variantSelection.variant;\n  const [selectedAddOnOptionIds, setSelectedAddOnOptionIds] = useState<string[]>([]);\n\n  const [compositeSelections, setCompositeSelections] = useState<ComponentSelectionInput[]>([]);\n  const [compositePrice, setCompositePrice] = useState<CompositePriceResult | null>(null);\n  const [compositeReady, setCompositeReady] = useState(false);\n\n  const [bundleSelections, setBundleSelections] = useState<BundleSelectionInput[]>([]);\n  const [bundleTotalPrice, setBundleTotalPrice] = useState<number | null>(null);\n  const [bundleReady, setBundleReady] = useState(false);\n  const [selectedBillingPlan, setSelectedBillingPlan] = useState<ProductBillingPlan | null>(null);\n  const [customerInputValues, setCustomerInputValues] = useState<Record<string, unknown>>({});\n  const [specialInstructions, setSpecialInstructions] = useState(\"\");\n  const [selectedSlot, setSelectedSlot] = useState<AvailableSlot | null>(null);\n  const [selectedStaffId, setSelectedStaffId] = useState<string | null>(null);\n  const [unitsCount, setUnitsCount] = useState(1);\n\n  const cart = useCart();\n\n  const productType = product.type || \"product\";\n  const isComposite = productType === \"composite\";\n  const isBundle = productType === \"bundle\";\n  const isDigital = productType === \"digital\";\n  const isService = productType === \"service\";\n  // Whole-stay services can reserve N interchangeable units (rooms, kayaks,\n  // pitches) per booking — mirrors desk's wizard, which gates the input the\n  // same way; intraday party size is already the quantity.\n  const offersUnits = isService && isMultiDayService(product);\n  const isStandard = !isComposite && !isBundle;\n\n  const hasVariants = isStandard && product.variants && product.variants.length > 0;\n  const hasAddOns = isStandard && product.add_ons && product.add_ons.length > 0;\n\n  // Staff offered for the chosen slot. SlotStaffInfo carries id + name; richer\n  // profiles (avatars/bios) would come from a staff roster fetch.\n  const slotStaff = useMemo<Staff[]>(\n    () =>\n      (selectedSlot?.available_staff ?? []).map((member) => ({\n        id: member.staff_id,\n        name: member.name,\n      })),\n    [selectedSlot],\n  );\n  const selectedStaffName = selectedStaffId\n    ? (slotStaff.find((member) => member.id === selectedStaffId)?.name ?? null)\n    : null;\n\n  const selectedAddOnOptions = useMemo(() => {\n    if (!product.add_ons) return [];\n    const options: AddOnOption[] = [];\n    for (const addOn of product.add_ons) {\n      for (const option of addOn.options) {\n        if (selectedAddOnOptionIds.includes(option.id)) {\n          options.push(option);\n        }\n      }\n    }\n    return options;\n  }, [product.add_ons, selectedAddOnOptionIds]);\n\n  const normalizedAddOnOptionIds = useMemo(() => {\n    if (selectedAddOnOptionIds.length === 0) return [];\n    return Array.from(\n      new Set(selectedAddOnOptionIds.map((id) => id.trim()).filter(Boolean)),\n    ).sort();\n  }, [selectedAddOnOptionIds]);\n\n  const localTotalPrice = useMemo(() => {\n    if (isComposite && compositePrice) {\n      return parsePrice(compositePrice.final_price) * quantity;\n    }\n\n    if (isBundle && bundleTotalPrice != null) {\n      return bundleTotalPrice * quantity;\n    }\n\n    let price = getVariantUnitPrice(product, selectedVariant);\n\n    for (const option of selectedAddOnOptions) {\n      if (option.default_price) {\n        price += parsePrice(option.default_price);\n      }\n    }\n\n    return price * quantity;\n  }, [\n    product.default_price,\n    selectedVariant,\n    selectedAddOnOptions,\n    quantity,\n    isComposite,\n    compositePrice,\n    isBundle,\n    bundleTotalPrice,\n  ]);\n\n  const requiredAddOnsSatisfied = useMemo(() => {\n    if (!product.add_ons) return true;\n\n    for (const addOn of product.add_ons) {\n      if (addOn.is_required) {\n        const selectedInGroup = selectedAddOnOptionIds.filter((id) =>\n          addOn.options.some((opt) => opt.id === id),\n        ).length;\n\n        const minRequired = addOn.min_selections || 1;\n        if (selectedInGroup < minRequired) {\n          return false;\n        }\n      }\n    }\n    return true;\n  }, [product.add_ons, selectedAddOnOptionIds]);\n\n  const quoteInput = useMemo(\n    () => ({\n      productId: product.id,\n      quantity,\n      variantId: selectedVariantId,\n      addOnOptionIds: normalizedAddOnOptionIds.length > 0 ? normalizedAddOnOptionIds : undefined,\n      bundleSelections: isBundle && bundleSelections.length > 0 ? bundleSelections : undefined,\n      compositeSelections:\n        isComposite && compositeSelections.length > 0 ? compositeSelections : undefined,\n    }),\n    [\n      product.id,\n      quantity,\n      selectedVariantId,\n      normalizedAddOnOptionIds,\n      isBundle,\n      bundleSelections,\n      isComposite,\n      compositeSelections,\n    ],\n  );\n\n  const requiredInputsSatisfied = useMemo(() => {\n    if (!product.input_fields || product.input_fields.length === 0) return true;\n    return product.input_fields.every((field) => {\n      if (!field.is_required) return true;\n      const val = customerInputValues[field.id];\n      return val !== undefined && val !== \"\" && val !== null;\n    });\n  }, [product.input_fields, customerInputValues]);\n\n  const quoteEnabled = isComposite\n    ? compositeReady && requiredInputsSatisfied\n    : isBundle\n      ? bundleReady && requiredInputsSatisfied\n      : requiredAddOnsSatisfied && requiredInputsSatisfied;\n\n  const {\n    quote,\n    isLoading: isQuoteLoading,\n    error: quoteError,\n    isExpired: isQuoteExpired,\n  } = useQuote(quoteInput, {\n    enabled: quoteEnabled,\n  });\n\n  const currency = quote?.currency ?? getProductCurrency(product);\n  const quoteId = quote?.quote_id;\n  const quotedUnitPrice = (quote?.quoted_total_price_info ?? quote?.final_price_info)?.final_price;\n  const quotedTotalPrice = useMemo(() => {\n    if (quotedUnitPrice === undefined || quotedUnitPrice === null) return undefined;\n    return parsePrice(quotedUnitPrice) * quantity;\n  }, [quotedUnitPrice, quantity]);\n\n  const quoteIsReady =\n    quoteEnabled && quote !== null && !isQuoteLoading && !quoteError && !isQuoteExpired;\n  const validationMessage = !quoteEnabled\n    ? REQUIRED_OPTIONS_MESSAGE\n    : quoteError\n      ? QUOTE_UNAVAILABLE_MESSAGE\n      : !quoteIsReady\n        ? QUOTE_LOADING_MESSAGE\n        : null;\n\n  // Services bill by basis × periods × units server-side, not by quantity —\n  // mirror billing_multiplier so the preview matches the cart. Both totals\n  // above already include × quantity, so divide it back out to the unit\n  // price before applying the real multiplier.\n  const displayTotalPrice = useMemo(() => {\n    const total = quotedTotalPrice ?? localTotalPrice;\n    const multiplier = serviceBillingMultiplier(product, {\n      quantity,\n      units: unitsCount,\n      scheduledStart: selectedSlot?.start_time,\n      scheduledEnd: selectedSlot?.end_time,\n    });\n    if (multiplier === null) return total;\n    return (total / Math.max(1, quantity)) * multiplier;\n  }, [quotedTotalPrice, localTotalPrice, product, quantity, unitsCount, selectedSlot]);\n\n  const handleVariantChange = useCallback(\n    (variantId: string | undefined, variant: VariantView | undefined) => {\n      setVariantSelection({ id: variantId, variant });\n      onVariantChange?.(variantId, variant);\n    },\n    [onVariantChange],\n  );\n\n  const addedTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n  useEffect(() => () => clearTimeout(addedTimerRef.current), []);\n\n  const handleAddToCart = useCallback(async () => {\n    if (\n      isSubmitting ||\n      !quoteIsReady ||\n      !quote ||\n      quotedUnitPrice === undefined ||\n      quotedUnitPrice === null\n    ) {\n      return;\n    }\n\n    setIsSubmitting(true);\n\n    const options: AddToCartOptions = {\n      locationId: quote.location_id ?? undefined,\n      variantId: selectedVariantId,\n      variant: selectedVariant\n        ? {\n            id: selectedVariant.id,\n            name: selectedVariant.name || \"\",\n            price_adjustment: selectedVariant.price_adjustment,\n            price_info: selectedVariant.price_info,\n          }\n        : undefined,\n      quoteId,\n      quotedUnitPrice,\n      addOnOptionIds: normalizedAddOnOptionIds.length > 0 ? normalizedAddOnOptionIds : undefined,\n      addOnOptions:\n        selectedAddOnOptions.length > 0\n          ? selectedAddOnOptions.map((opt) => ({\n              id: opt.id,\n              name: opt.name,\n              add_on_id: opt.add_on_id,\n              default_price: opt.default_price,\n            }))\n          : undefined,\n      compositeSelections:\n        isComposite && compositeSelections.length > 0 ? compositeSelections : undefined,\n      bundleSelections: isBundle && bundleSelections.length > 0 ? bundleSelections : undefined,\n      billingPlanId: selectedBillingPlan?.id,\n      ...(isService && selectedSlot\n        ? {\n            scheduledStart: selectedSlot.start_time,\n            scheduledEnd: selectedSlot.end_time,\n            staffId: selectedStaffId ?? selectedSlot.available_staff?.[0]?.staff_id,\n            resourceId: selectedSlot.available_resources?.[0]?.resource_id,\n            units: unitsCount > 1 ? unitsCount : undefined,\n          }\n        : {}),\n      customerInputs:\n        Object.keys(customerInputValues).length > 0\n          ? Object.entries(customerInputValues)\n              .filter(([, v]) => v !== undefined && v !== \"\")\n              .map(([fieldId, value]) => ({ field_id: fieldId, value }))\n          : undefined,\n      specialInstructions: specialInstructions.trim() || undefined,\n    };\n\n    try {\n      if (onAddToCart) {\n        await onAddToCart(product, quantity, options);\n      } else {\n        await cart.addItem(product, quantity, options);\n      }\n      setIsAdded(true);\n      clearTimeout(addedTimerRef.current);\n      addedTimerRef.current = setTimeout(() => {\n        setIsAdded(false);\n        setQuantity(product.min_order_quantity ?? 1);\n      }, 2000);\n    } catch {\n      // Caller handles errors via onAddToCart or cart hook error state\n    } finally {\n      setIsSubmitting(false);\n    }\n  }, [\n    product,\n    quantity,\n    selectedVariantId,\n    selectedVariant,\n    quoteId,\n    quotedUnitPrice,\n    normalizedAddOnOptionIds,\n    selectedAddOnOptions,\n    isComposite,\n    compositeSelections,\n    isBundle,\n    bundleSelections,\n    selectedBillingPlan,\n    customerInputValues,\n    specialInstructions,\n    isService,\n    selectedSlot,\n    selectedStaffId,\n    unitsCount,\n    isSubmitting,\n    onAddToCart,\n    cart,\n    quoteIsReady,\n    quote,\n  ]);\n\n  return (\n    <div data-cimplify-customizer className={cn(\"space-y-6\", className, classNames?.root)}>\n      {isComposite && product.groups && product.composite_id && (\n        <CompositeSelector\n          compositeId={product.composite_id}\n          groups={product.groups}\n          currency={currency}\n          onSelectionsChange={setCompositeSelections}\n          onPriceChange={setCompositePrice}\n          onReady={setCompositeReady}\n          skipPriceFetch\n        />\n      )}\n\n      {isBundle && product.components && (\n        <BundleSelector\n          components={product.components}\n          bundlePrice={product.bundle_price}\n          discountValue={product.discount_value}\n          pricingType={product.pricing_type}\n          currency={currency}\n          onSelectionsChange={setBundleSelections}\n          onPriceChange={setBundleTotalPrice}\n          onReady={setBundleReady}\n        />\n      )}\n\n      {isService && (\n        <DateSlotPicker\n          serviceId={product.id}\n          selectedSlot={selectedSlot}\n          onSlotSelect={(slot) => {\n            setSelectedSlot(slot);\n            setSelectedStaffId(null);\n          }}\n          participantCount={quantity}\n          units={offersUnits ? unitsCount : undefined}\n          currency={currency}\n          // Without the resolved mode the picker renders intraday slots for\n          // whole-stay services — stays need the stay-range treatment.\n          schedulingMode={product.scheduling_mode ?? (offersUnits ? \"multi_day\" : \"intraday\")}\n          durationUnit={product.duration_unit}\n          durationValue={product.duration_value}\n        />\n      )}\n\n      {offersUnits && (\n        <div data-cimplify-customizer-units className={classNames?.units}>\n          <label className=\"block text-xs font-medium uppercase tracking-wider text-muted-foreground mb-3\">\n            Units\n          </label>\n          <QuantitySelector value={unitsCount} onChange={setUnitsCount} min={1} />\n        </div>\n      )}\n\n      {isService && slotStaff.length > 0 && (\n        <div data-cimplify-customizer-staff>\n          <label className=\"block text-xs font-medium uppercase tracking-wider text-muted-foreground mb-3\">\n            Choose your specialist\n          </label>\n          <StaffPicker\n            staff={slotStaff}\n            selectedStaffId={selectedStaffId}\n            onStaffSelect={setSelectedStaffId}\n            className=\"flex flex-wrap gap-2\"\n            classNames={{\n              option:\n                \"inline-flex items-center gap-2 rounded-md border border-border px-3 py-2 text-sm font-medium transition-colors hover:border-foreground/40 cursor-pointer data-[selected]:border-foreground data-[selected]:bg-foreground data-[selected]:text-background\",\n              avatar: \"w-6 h-6 rounded-full object-cover\",\n            }}\n          />\n        </div>\n      )}\n\n      {hasVariants && (\n        <VariantSelector\n          variants={product.variants!}\n          variantAxes={product.variant_axes}\n          basePrice={product.default_price}\n          currency={currency}\n          selectedVariantId={selectedVariantId}\n          onVariantChange={handleVariantChange}\n          productName={product.name}\n        />\n      )}\n\n      {hasAddOns && (\n        <AddOnSelector\n          addOns={product.add_ons!}\n          selectedOptions={selectedAddOnOptionIds}\n          onOptionsChange={setSelectedAddOnOptionIds}\n          currency={currency}\n        />\n      )}\n\n      {/* Billing plans */}\n      {product.billing_plans && product.billing_plans.length > 0 && (\n        <BillingPlanSelector\n          productId={product.id}\n          plans={product.billing_plans}\n          currency={currency}\n          onPlanSelect={setSelectedBillingPlan}\n          selectedPlanId={selectedBillingPlan?.id ?? null}\n          showOneTimePurchase\n        />\n      )}\n\n      {/* Volume pricing tiers */}\n      {product.quantity_pricing && product.quantity_pricing.length > 1 && (\n        <VolumePricing\n          tiers={product.quantity_pricing}\n          currentQuantity={quantity}\n          currency={currency}\n        />\n      )}\n\n      {/* Customer input fields */}\n      {product.input_fields && product.input_fields.length > 0 && (\n        <CustomerInputFields\n          fields={product.input_fields}\n          values={customerInputValues}\n          onChange={setCustomerInputValues}\n          currency={currency}\n        />\n      )}\n\n      {/* Deposit info for service products */}\n      {product.deposit_type && product.deposit_type !== \"none\" && product.deposit_amount && (\n        <div\n          data-cimplify-customizer-deposit\n          className={cn(\"text-sm text-muted-foreground\", classNames?.depositInfo)}\n        >\n          {product.deposit_type === \"fixed\" ? (\n            <span>\n              Deposit required: <Price amount={product.deposit_amount} currency={currency} />\n            </span>\n          ) : (\n            <span>Deposit required: {parsePrice(product.deposit_amount)}%</span>\n          )}\n        </div>\n      )}\n\n      {/* Allergens */}\n      {product.allergies && product.allergies.length > 0 && (\n        <div\n          data-cimplify-customizer-allergens\n          className={cn(\"flex flex-wrap gap-1.5\", classNames?.allergens)}\n        >\n          {product.allergies.map((allergen) => (\n            <span\n              key={allergen}\n              data-cimplify-allergen-tag\n              className=\"inline-block text-xs px-2 py-0.5 rounded-full bg-muted text-muted-foreground\"\n            >\n              {allergen}\n            </span>\n          ))}\n        </div>\n      )}\n\n      {/* Service duration */}\n      {isService && product.duration_minutes != null && (\n        <div\n          data-cimplify-customizer-duration\n          className={cn(\"text-sm text-muted-foreground\", classNames?.duration)}\n        >\n          Duration: {formatDuration(product.duration_minutes, product.duration_unit)}\n        </div>\n      )}\n\n      {/* Special instructions */}\n      {showSpecialInstructions && !isDigital && (\n        <div data-cimplify-customizer-special-instructions>\n          <textarea\n            value={specialInstructions}\n            onChange={(e) => setSpecialInstructions(e.target.value)}\n            placeholder=\"Special instructions (e.g., no onions, extra sauce)\"\n            rows={2}\n            data-cimplify-customizer-textarea\n            className={cn(\n              \"w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring resize-none\",\n              classNames?.specialInstructions,\n            )}\n          />\n        </div>\n      )}\n\n      {isService && selectedSlot && (\n        <BookingSummary\n          slot={selectedSlot}\n          total={displayTotalPrice}\n          staffName={selectedStaffName}\n          durationMinutes={product.duration_minutes}\n          depositType={product.deposit_type}\n          depositAmount={product.deposit_amount}\n          currency={currency}\n        />\n      )}\n\n      <div\n        data-cimplify-customizer-actions\n        className={cn(\"pt-4 border-t border-border\", classNames?.actions)}\n      >\n        {validationMessage && (\n          <p\n            id={CUSTOMIZER_VALIDATION_ID}\n            data-cimplify-customizer-validation\n            className={cn(\n              \"text-sm mb-3\",\n              quoteError ? \"text-destructive\" : \"text-muted-foreground\",\n              classNames?.validation,\n            )}\n          >\n            {validationMessage}\n          </p>\n        )}\n        <div className=\"flex items-center gap-4\">\n          <div className=\"flex flex-col gap-1\">\n            <QuantitySelector\n              value={quantity}\n              onChange={setQuantity}\n              min={product.min_order_quantity ?? 1}\n            />\n            {product.min_order_quantity && product.min_order_quantity > 1 && (\n              <span\n                data-cimplify-customizer-min-hint\n                className=\"text-xs text-muted-foreground text-center\"\n              >\n                Min. {product.min_order_quantity}\n              </span>\n            )}\n          </div>\n\n          {product.quantity_pricing && product.quantity_pricing.length > 0 && (\n            <div\n              data-cimplify-customizer-unit-price\n              className=\"text-sm text-muted-foreground text-right shrink-0\"\n            >\n              <Price\n                amount={getUnitPriceAtQuantity(\n                  product.quantity_pricing,\n                  quantity,\n                  parsePrice(product.default_price),\n                )}\n                currency={currency}\n                className=\"font-medium text-foreground\"\n              />{\" \"}\n              ea.\n            </div>\n          )}\n\n          <Button\n            onClick={handleAddToCart}\n            disabled={isAdded || isSubmitting || !quoteIsReady}\n            aria-describedby={validationMessage ? CUSTOMIZER_VALIDATION_ID : undefined}\n            data-cimplify-customizer-submit\n            className={cn(\n              \"flex-1 h-14 text-base bg-primary text-primary-foreground font-medium hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed rounded-full\",\n              isAdded && classNames?.submitButtonAdded,\n              classNames?.submitButton,\n            )}\n          >\n            {isAdded ? (\n              \"Added\"\n            ) : !quoteEnabled ? (\n              (submitLabel ?? \"Add to Cart\")\n            ) : !quoteIsReady ? (\n              quoteError ? (\n                QUOTE_UNAVAILABLE_MESSAGE\n              ) : (\n                QUOTE_LOADING_MESSAGE\n              )\n            ) : (\n              <>\n                {submitLabel ?? \"Add to Cart\"} &middot;{\" \"}\n                <Price amount={displayTotalPrice} currency={currency} />\n              </>\n            )}\n          </Button>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
    }
  ]
}
