{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "product-attributes",
  "title": "Product Attributes",
  "description": "Renders a product's extracted attributes (structured_attributes plus materials, gender, and age) as a two-column definition list.",
  "dependencies": [
    "@channel3/sdk"
  ],
  "files": [
    {
      "path": "registry/default/components/product-attributes.tsx",
      "content": "import * as React from \"react\";\nimport type { Product } from \"@channel3/sdk/resources\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface Attribute {\n  label: string;\n  value: string;\n}\n\nconst unique = <T,>(values: T[]): T[] => Array.from(new Set(values));\n\nconst humanizeKey = (key: string) =>\n  key\n    .replace(/[_-]+/g, \" \")\n    .trim()\n    .replace(/\\b\\w/g, (char) => char.toUpperCase());\n\nconst titleCase = (value: string) => value.charAt(0).toUpperCase() + value.slice(1);\n\n/**\n * Flattens a product's extracted attributes into a deduplicated list of\n * label/value rows: `category`, then each `structured_attributes` entry,\n * `materials`, `gender`, and `age`. A `material(s)` structured entry is dropped\n * when the richer `materials` array is present so the same fact isn't shown\n * twice.\n */\nfunction buildAttributes(product: Product): Attribute[] {\n  const rows: Attribute[] = [];\n\n  if (product.category?.title) {\n    rows.push({ label: \"Category\", value: product.category.title });\n  }\n\n  const materials = unique((product.materials ?? []).map((value) => value.trim()).filter(Boolean));\n  const hasMaterials = materials.length > 0;\n\n  for (const [key, values] of Object.entries(product.structured_attributes ?? {})) {\n    const normalized = key.replace(/[_-]+/g, \"\").toLowerCase();\n    if (hasMaterials && (normalized === \"material\" || normalized === \"materials\")) {\n      continue;\n    }\n    const cleaned = unique((values ?? []).map((value) => String(value).trim()).filter(Boolean));\n    if (cleaned.length === 0) {\n      continue;\n    }\n    rows.push({ label: humanizeKey(key), value: cleaned.join(\", \") });\n  }\n\n  if (hasMaterials) {\n    rows.push({\n      label: materials.length > 1 ? \"Materials\" : \"Material\",\n      value: materials.join(\", \"),\n    });\n  }\n  if (product.gender) {\n    rows.push({ label: \"Gender\", value: titleCase(product.gender) });\n  }\n  if (product.age) {\n    rows.push({ label: \"Age\", value: titleCase(product.age) });\n  }\n\n  return rows;\n}\n\nexport interface ProductAttributesProps extends React.ComponentProps<\"dl\"> {\n  product: Product;\n}\n\nexport function ProductAttributes({ product, className, ...props }: ProductAttributesProps) {\n  const attributes = buildAttributes(product);\n  if (attributes.length === 0) {\n    return null;\n  }\n\n  return (\n    <dl\n      data-slot=\"product-attributes\"\n      className={cn(\n        \"grid grid-cols-[minmax(5rem,auto)_1fr] gap-x-6 gap-y-2 text-sm\",\n        className,\n      )}\n      {...props}\n    >\n      {attributes.map(({ label, value }) => (\n        <React.Fragment key={label}>\n          <dt className=\"text-muted-foreground\">{label}</dt>\n          <dd className=\"text-foreground\">{value}</dd>\n        </React.Fragment>\n      ))}\n    </dl>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}