{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "frequently-bought-together",
  "title": "Frequently Bought Together",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-react/blocks/frequently-bought-together/FrequentlyBoughtTogether.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { Plus, ShoppingBag, Truck } from 'lucide-react'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card } from '@/components/ui/card'\nimport { Checkbox } from '@/components/ui/checkbox'\nimport { cn } from '@/lib/utils'\n\ninterface BundleItem {\n  id: string\n  name: string\n  subtitle: string\n  price: number\n  image: string\n  isMain?: boolean\n}\n\nconst items: BundleItem[] = [\n  {\n    id: 'headphones',\n    name: 'Pro Studio Headphones',\n    subtitle: 'Space Black · Over-Ear Wireless ANC',\n    price: 299.0,\n    image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=500&auto=format&fit=crop&q=80',\n    isMain: true,\n  },\n  {\n    id: 'case',\n    name: 'Premium Hard Shell Case',\n    subtitle: 'Weather-resistant EVA shell with plush velvet lining',\n    price: 45.0,\n    image: 'https://images.unsplash.com/photo-1583394838336-acd977736f90?w=500&auto=format&fit=crop&q=80',\n  },\n  {\n    id: 'cable',\n    name: 'Braided 3.5mm Audiophile Cable',\n    subtitle: '1.5m Silver-plated OFC copper with gold-plated jacks',\n    price: 25.0,\n    image: 'https://images.unsplash.com/photo-1546435770-a3e426bf472b?w=500&auto=format&fit=crop&q=80',\n  },\n]\n\nfunction formatCurrency(val: number) {\n  return '$' + val.toFixed(2)\n}\n\nexport function FrequentlyBoughtTogether() {\n  const [selectedIds, setSelectedIds] = React.useState<string[]>(['headphones', 'case', 'cable'])\n\n  function toggleItem(id: string) {\n    setSelectedIds((prev) => (prev.includes(id) ? prev.filter((itemId) => itemId !== id) : [...prev, id]))\n  }\n\n  const selectedItems = React.useMemo(() => items.filter((item) => selectedIds.includes(item.id)), [selectedIds])\n  const selectedCount = selectedItems.length\n\n  const originalTotal = React.useMemo(() => selectedItems.reduce((sum, item) => sum + item.price, 0), [selectedItems])\n\n  const discountRate = selectedCount >= 2 ? 0.15 : 0\n  const discountAmount = originalTotal * discountRate\n  const finalPrice = originalTotal - discountAmount\n  const hasDiscount = discountRate > 0\n\n  const buttonText = React.useMemo(() => {\n    if (selectedCount === items.length) return `Add all ${items.length} to Cart`\n    if (selectedCount > 1) return `Add ${selectedCount} selected to Cart`\n    if (selectedCount === 1) return 'Add 1 selected to Cart'\n    return 'Select items to add'\n  }, [selectedCount])\n\n  const deliveryNote = React.useMemo(() => {\n    if (selectedCount >= 2) return 'Free express delivery included with this bundle'\n    if (selectedCount === 1) return 'Standard delivery included'\n    return 'Select items to view delivery options'\n  }, [selectedCount])\n\n  return (\n    <Card data-slot=\"frequently-bought-together\" className=\"mx-auto w-full max-w-4xl space-y-6 p-6 shadow-xs\">\n      {/* Header */}\n      <div className=\"space-y-1.5\">\n        <h2 className=\"text-foreground text-xl font-bold tracking-tight sm:text-2xl\">Frequently Bought Together</h2>\n        <p className=\"text-muted-foreground text-sm\">\n          Pair with these recommended essentials for maximum performance and save 15% on the bundle\n        </p>\n      </div>\n\n      {/* Product Visual Row */}\n      <div className=\"grid grid-cols-1 items-center gap-3 sm:grid-cols-[1fr_auto_1fr_auto_1fr] sm:gap-4\">\n        {items.map((item, index) => (\n          <React.Fragment key={item.id}>\n            <div\n              className={cn(\n                'border-border bg-muted/20 relative flex flex-col rounded-xl border p-3 text-left transition-colors duration-200 sm:p-4',\n                selectedIds.includes(item.id) ? 'opacity-100' : 'opacity-40 grayscale',\n              )}\n            >\n              <div className=\"border-border bg-background relative mb-3 aspect-square w-full overflow-hidden rounded-lg border\">\n                <img src={item.image} alt={item.name} className=\"size-full object-cover\" />\n                {item.isMain && (\n                  <Badge\n                    variant=\"secondary\"\n                    className=\"bg-background/90 text-foreground absolute top-2 left-2 text-xs font-medium backdrop-blur-xs\"\n                  >\n                    This item\n                  </Badge>\n                )}\n              </div>\n              <span className=\"text-foreground line-clamp-1 text-xs leading-tight font-semibold sm:text-sm\">\n                {item.name}\n              </span>\n              <span className=\"text-foreground mt-1 text-xs font-semibold tabular-nums sm:text-sm\">\n                {formatCurrency(item.price)}\n              </span>\n            </div>\n\n            {index < items.length - 1 && (\n              <div\n                className=\"border-border/50 text-muted-foreground bg-muted mx-auto flex size-8 shrink-0 items-center justify-center rounded-full border\"\n                aria-hidden=\"true\"\n              >\n                <Plus className=\"size-4\" />\n              </div>\n            )}\n          </React.Fragment>\n        ))}\n      </div>\n\n      {/* Item Selection Checkbox List */}\n      <div className=\"space-y-2.5\">\n        {items.map((item) => (\n          <div\n            key={item.id}\n            role=\"button\"\n            tabIndex={0}\n            className={cn(\n              'border-border focus-visible:ring-ring flex cursor-pointer items-start justify-between gap-3 rounded-lg border p-3.5 transition-colors select-none focus-visible:ring-2 focus-visible:outline-none sm:items-center',\n              selectedIds.includes(item.id)\n                ? 'bg-card hover:border-primary/40'\n                : 'border-border/60 bg-muted/10 opacity-60 hover:opacity-80',\n            )}\n            onClick={() => toggleItem(item.id)}\n            onKeyDown={(e) => {\n              if (e.key === ' ' || e.key === 'Enter') {\n                e.preventDefault()\n                toggleItem(item.id)\n              }\n            }}\n          >\n            <div className=\"flex items-start gap-3 sm:items-center\">\n              <Checkbox\n                id={item.id}\n                checked={selectedIds.includes(item.id)}\n                className=\"pointer-events-none mt-0.5 sm:mt-0\"\n                tabIndex={-1}\n              />\n              <div className=\"space-y-0.5\">\n                <div className=\"flex flex-wrap items-center gap-2\">\n                  <span className=\"text-foreground text-sm font-medium\">\n                    {item.isMain && <span className=\"font-semibold\">This item: </span>}\n                    {item.name}\n                  </span>\n                  {item.isMain && (\n                    <Badge variant=\"secondary\" className=\"text-xs\">\n                      Main Item\n                    </Badge>\n                  )}\n                </div>\n                <p className=\"text-muted-foreground text-xs\">{item.subtitle}</p>\n              </div>\n            </div>\n            <span className=\"text-foreground shrink-0 text-sm font-semibold tabular-nums\">\n              {formatCurrency(item.price)}\n            </span>\n          </div>\n        ))}\n      </div>\n\n      {/* Price & Bundle Checkout Summary Box */}\n      <div className=\"border-border bg-muted/30 flex flex-col gap-4 rounded-xl border p-4 sm:p-5 md:flex-row md:items-center md:justify-between\">\n        <div className=\"space-y-1.5\">\n          <div className=\"text-muted-foreground text-xs font-medium tracking-wider uppercase\">\n            {selectedCount >= 2 ? 'Bundle Price' : 'Total Price'}\n          </div>\n          <div className=\"flex flex-wrap items-baseline gap-2.5\">\n            <span className=\"text-foreground text-2xl font-bold tracking-tight tabular-nums sm:text-3xl\">\n              {formatCurrency(finalPrice)}\n            </span>\n            {hasDiscount && (\n              <span className=\"text-muted-foreground text-sm font-medium tabular-nums line-through sm:text-base\">\n                {formatCurrency(originalTotal)}\n              </span>\n            )}\n            {hasDiscount && (\n              <Badge variant=\"outline\" className=\"border-success/30 bg-success/10 text-success text-xs font-medium\">\n                Save {formatCurrency(discountAmount)} (15% Bundle Discount)\n              </Badge>\n            )}\n          </div>\n          <div className=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n            <Truck className=\"text-success size-3.5 shrink-0\" />\n            <span>{deliveryNote}</span>\n          </div>\n        </div>\n\n        <Button size=\"lg\" disabled={selectedCount === 0} className=\"w-full gap-2 font-semibold shadow-xs md:w-auto\">\n          <ShoppingBag className=\"size-4\" />\n          <span>{buttonText}</span>\n        </Button>\n      </div>\n    </Card>\n  )\n}\n",
      "type": "registry:block",
      "target": "~/components/blocks/FrequentlyBoughtTogether.tsx"
    }
  ],
  "dependencies": [
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/react/badge.json",
    "https://uipkge.dev/r/react/button.json",
    "https://uipkge.dev/r/react/card.json",
    "https://uipkge.dev/r/react/checkbox.json"
  ],
  "description": "E-commerce frequently bought together bundle builder with interactive product preview cards, checkbox selection, dynamic bundle discount calculation, and one-click cart checkout.",
  "categories": [
    "commerce",
    "ecommerce"
  ]
}