{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "discount-input",
  "title": "Discount Input",
  "description": "Discount input primitive.",
  "registryDependencies": [
    "@circle-ui/badge",
    "@circle-ui/button",
    "@circle-ui/textbox",
    "@circle-ui/utils"
  ],
  "files": [
    {
      "path": "registry/berlin/blocks/commerce.tsx",
      "content": "// Generated from packages/ui/src/components/commerce.tsx\n\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/registry/berlin/lib/utils\";\nimport { Badge, type BadgeProps } from \"@/registry/berlin/circle-ui/badge\";\nimport { Button, type ButtonProps } from \"@/registry/berlin/circle-ui/button\";\nimport { TextBox } from \"@/registry/berlin/circle-ui/textbox\";\n\nfunction PlusIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      className={className}\n      viewBox=\"0 0 16 16\"\n      fill=\"none\"\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M8 3.5v9m4.5-4.5h-9\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.5\"\n        strokeLinecap=\"round\"\n      />\n    </svg>\n  );\n}\n\nfunction MinusIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      className={className}\n      viewBox=\"0 0 16 16\"\n      fill=\"none\"\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M3.5 8h9\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.5\"\n        strokeLinecap=\"round\"\n      />\n    </svg>\n  );\n}\n\nfunction BinIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      className={className}\n      viewBox=\"0 0 16 16\"\n      fill=\"none\"\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M3.5 5.5h9m-7 0V4.25A1.25 1.25 0 0 1 6.75 3h2.5A1.25 1.25 0 0 1 10.5 4.25V5.5m-5.5 0 0.5 6A1.5 1.5 0 0 0 7 13h2a1.5 1.5 0 0 0 1.5-1.5l.5-6\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.5\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\ninterface PricingDisplayProps {\n  finalPrice?: string;\n  initialPrice?: string;\n  savings?: string;\n}\n\nexport interface ProductItemProps extends React.HTMLAttributes<HTMLDivElement> {\n  image?: string;\n  title: string;\n  pricing?: PricingDisplayProps;\n}\n\nfunction PricingDisplay({\n  finalPrice,\n  initialPrice,\n  savings,\n}: PricingDisplayProps) {\n  return (\n    <div className=\"flex flex-wrap items-center gap-2 text-sm leading-6\">\n      {finalPrice ? <p className=\"font-semibold\">{finalPrice}</p> : null}\n      {initialPrice ? (\n        <p className=\"text-muted-foreground line-through\">{initialPrice}</p>\n      ) : null}\n      {savings ? <p className=\"font-medium text-primary\">{savings}</p> : null}\n    </div>\n  );\n}\n\nexport function ProductItem({\n  image,\n  title,\n  pricing,\n  children,\n  className,\n  ...props\n}: ProductItemProps) {\n  return (\n    <div className={cn(\"flex items-center gap-4\", className)} {...props}>\n      {image ? (\n        <img\n          className=\"size-16 rounded-xl object-cover\"\n          src={image}\n          alt={title}\n        />\n      ) : null}\n      <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n        <div className=\"text-base font-medium leading-6\">{title}</div>\n        {children ? (\n          <div className=\"text-sm leading-6 text-muted-foreground\">\n            {children}\n          </div>\n        ) : null}\n        {pricing ? <PricingDisplay {...pricing} /> : null}\n      </div>\n    </div>\n  );\n}\n\nexport interface ProductCardProps extends ProductItemProps {\n  badge?: BadgeProps;\n  button?: ButtonProps;\n}\n\nexport function ProductCard({\n  badge,\n  button,\n  className,\n  ...props\n}: ProductCardProps) {\n  return (\n    <div\n      className={cn(\n        \"space-y-4 rounded-[var(--border-radius)] border bg-card p-4\",\n        className,\n      )}\n    >\n      {badge ? (\n        <div className=\"flex justify-start\">\n          <Badge {...badge} />\n        </div>\n      ) : null}\n      <ProductItem {...props} />\n      {button ? <Button size=\"small\" theme=\"light\" {...button} /> : null}\n    </div>\n  );\n}\n\ntype TableRowVariant = \"highlight\" | \"default\" | \"total\" | \"totalLarge\";\n\nexport interface TableRowProps extends React.HTMLAttributes<HTMLDivElement> {\n  description: string;\n  amount: string;\n  variant?: TableRowVariant;\n  truncateValue?: boolean;\n}\n\nexport function PricingTableRow({\n  description,\n  amount,\n  variant = \"default\",\n  truncateValue = false,\n  className,\n  ...props\n}: TableRowProps) {\n  return (\n    <div\n      className={cn(\n        \"flex items-center justify-between gap-4 py-3 text-sm leading-6\",\n        variant === \"highlight\" && \"rounded-xl bg-secondary px-3 font-medium\",\n        variant === \"total\" && \"border-t pt-4 font-semibold\",\n        variant === \"totalLarge\" && \"border-t pt-4 text-base font-semibold\",\n        className,\n      )}\n      {...props}\n    >\n      <div>{description}</div>\n      <div className={cn(truncateValue && \"max-w-[140px] truncate text-right\")}>\n        {amount}\n      </div>\n    </div>\n  );\n}\n\nexport interface PricingTableProps extends React.HTMLAttributes<HTMLDivElement> {\n  lines?: TableRowProps[];\n  children?: React.ReactNode;\n}\n\nexport function PricingTable({\n  lines,\n  className,\n  children,\n  ...props\n}: PricingTableProps) {\n  return (\n    <div\n      className={cn(\n        \"rounded-[var(--border-radius)] border bg-card p-4\",\n        className,\n      )}\n      {...props}\n    >\n      {children ??\n        lines?.map((line, index) => <PricingTableRow key={index} {...line} />)}\n    </div>\n  );\n}\n\nexport interface QuantityAdjustorProps extends Omit<\n  React.HTMLAttributes<HTMLDivElement>,\n  \"onChange\"\n> {\n  disabled?: boolean;\n  value: number;\n  onChange: (value: number) => void;\n  onRemove?: () => void;\n  min?: number;\n  max?: number;\n  renderRemoved?: (restore: () => void) => React.ReactNode;\n}\n\nexport function QuantityAdjustor({\n  disabled = false,\n  value,\n  onChange,\n  onRemove,\n  min = 0,\n  max = Infinity,\n  className,\n  renderRemoved,\n  ...props\n}: QuantityAdjustorProps) {\n  const restore = React.useCallback(() => onChange(1), [onChange]);\n\n  if (value === 0) {\n    return (\n      <>\n        {renderRemoved ? (\n          renderRemoved(restore)\n        ) : (\n          <button\n            type=\"button\"\n            onClick={restore}\n            className=\"rounded-full border border-dashed px-4 py-2 text-sm text-primary\"\n          >\n            Add Item\n          </button>\n        )}\n      </>\n    );\n  }\n\n  return (\n    <div\n      className={cn(\n        \"inline-flex h-10 items-center rounded-full border border-subtle-border px-1 text-sm text-muted-foreground\",\n        disabled && \"opacity-50\",\n        className,\n      )}\n      {...props}\n    >\n      <button\n        type=\"button\"\n        disabled={disabled}\n        onClick={() => {\n          if (value <= min) return;\n          const next = value - 1;\n          if (next === 0) {\n            onRemove ? onRemove() : onChange(0);\n            return;\n          }\n          onChange(next);\n        }}\n        className=\"inline-flex size-8 items-center justify-center rounded-full\"\n      >\n        {value > 1 ? (\n          <MinusIcon className=\"size-4\" />\n        ) : (\n          <BinIcon className=\"size-4\" />\n        )}\n      </button>\n      <div className=\"min-w-8 text-center\">{value}</div>\n      <button\n        type=\"button\"\n        disabled={disabled || value >= max}\n        onClick={() => onChange(Math.min(value + 1, max))}\n        className=\"inline-flex size-8 items-center justify-center rounded-full\"\n      >\n        <PlusIcon className=\"size-4\" />\n      </button>\n    </div>\n  );\n}\n\nexport interface CartLineItemProps extends QuantityAdjustorProps {\n  product: ProductItemProps;\n}\n\nexport function CartLineItem({\n  product,\n  className,\n  ...props\n}: CartLineItemProps) {\n  return (\n    <div\n      className={cn(\n        \"flex items-center justify-between gap-4 rounded-[var(--border-radius)] border bg-card p-4\",\n        className,\n      )}\n    >\n      <ProductItem {...product} className=\"flex-1\" />\n      <QuantityAdjustor {...props} />\n    </div>\n  );\n}\n\nexport interface SubmitButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n  name?: string;\n  text: string;\n  state?: \"clear\" | \"error\" | \"success\" | \"default\" | \"notEmpty\";\n}\n\nfunction SubmitButton({\n  text,\n  className,\n  disabled,\n  ...props\n}: SubmitButtonProps) {\n  return (\n    <button\n      type=\"submit\"\n      disabled={disabled}\n      className={cn(\n        \"rounded-full bg-primary px-4 py-2 text-sm font-medium text-primary-foreground disabled:opacity-60\",\n        className,\n      )}\n      {...props}\n    >\n      {text}\n    </button>\n  );\n}\n\nexport interface DiscountInputProps {\n  input: {\n    label: string;\n    name: string;\n    placeholder: string;\n  };\n  button: SubmitButtonProps;\n  className?: string;\n  initialValues?: Record<string, string>;\n  asyncCheck: (\n    data: Record<string, string>,\n  ) => Promise<{ message?: string } | unknown>;\n}\n\nexport function DiscountInput({\n  input,\n  button,\n  className,\n  initialValues,\n  asyncCheck,\n}: DiscountInputProps) {\n  const [value, setValue] = React.useState(initialValues?.[input.name] ?? \"\");\n  const [status, setStatus] = React.useState<\n    \"idle\" | \"loading\" | \"success\" | \"error\"\n  >(\"idle\");\n  const [message, setMessage] = React.useState<string | null>(null);\n\n  return (\n    <form\n      className={cn(\"flex items-start gap-3\", className)}\n      onSubmit={async (event) => {\n        event.preventDefault();\n        setStatus(\"loading\");\n        setMessage(null);\n        try {\n          const result = await asyncCheck({ [input.name]: value });\n          setStatus(\"success\");\n          setMessage(\n            (result as { message?: string })?.message ?? \"Discount applied\",\n          );\n        } catch (error) {\n          setStatus(\"error\");\n          setMessage(\n            error instanceof Error ? error.message : \"Could not apply discount\",\n          );\n        }\n      }}\n    >\n      <div className=\"flex-1\">\n        <TextBox\n          name={input.name}\n          label={input.label}\n          placeholder={input.placeholder}\n          value={value}\n          error={status === \"error\" ? (message ?? undefined) : undefined}\n          onChange={(event) => setValue(event.target.value)}\n        />\n        {status === \"success\" && message ? (\n          <div className=\"mt-2 text-sm text-success\">{message}</div>\n        ) : null}\n      </div>\n      <SubmitButton\n        {...button}\n        text={status === \"loading\" ? \"Validating...\" : button.text}\n        disabled={status === \"loading\" || !value}\n      />\n    </form>\n  );\n}\n\nexport interface InputDiscountProps extends Omit<\n  React.InputHTMLAttributes<HTMLInputElement>,\n  \"defaultValue\" | \"name\" | \"onChange\" | \"value\"\n> {\n  name: string;\n  error?: string;\n  label?: string;\n  placeholder?: string;\n  successMessage?: string;\n  errorMessage?: string;\n  validCode?: string;\n  buttonText?: string;\n  buttonValidatingText?: string;\n  onApplyCode?: (code: string, isValid: boolean) => void;\n}\n\nexport function InputDiscount({\n  name,\n  error,\n  label,\n  placeholder,\n  successMessage = \"Discount code applied successfully!\",\n  errorMessage = \"Invalid discount code\",\n  validCode = \"VALID\",\n  buttonText = \"Apply\",\n  buttonValidatingText = \"Validating...\",\n  onApplyCode,\n  className,\n  ...props\n}: InputDiscountProps) {\n  const [inputValue, setInputValue] = React.useState(\"\");\n  const [isValidating, setIsValidating] = React.useState(false);\n  const [result, setResult] = React.useState<{\n    success?: string;\n    error?: string;\n  }>({});\n\n  async function handleApply() {\n    if (!inputValue) return;\n    setIsValidating(true);\n    setResult({});\n    await new Promise((resolve) => setTimeout(resolve, 400));\n    const isValid = inputValue.toUpperCase() === validCode.toUpperCase();\n    const next = isValid\n      ? { success: successMessage }\n      : { error: errorMessage };\n    setResult(next);\n    setIsValidating(false);\n    onApplyCode?.(inputValue, isValid);\n  }\n\n  return (\n    <div className={cn(\"space-y-3\", className)}>\n      <div className=\"relative\">\n        <TextBox\n          {...props}\n          name={name}\n          label={label}\n          placeholder={placeholder}\n          value={inputValue}\n          error={error || result.error}\n          onChange={(event) => {\n            setInputValue(event.target.value);\n            if (result.success || result.error) setResult({});\n          }}\n        />\n        <button\n          type=\"button\"\n          onClick={handleApply}\n          disabled={isValidating || !inputValue}\n          className=\"absolute right-3 top-1/2 -translate-y-1/2 rounded-full bg-primary px-4 py-2 text-sm font-medium text-primary-foreground disabled:opacity-60\"\n        >\n          {isValidating ? buttonValidatingText : buttonText}\n        </button>\n      </div>\n      {result.success ? (\n        <div className=\"text-sm text-success\">{result.success}</div>\n      ) : null}\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "src/components/ui/commerce.tsx"
    }
  ],
  "type": "registry:ui"
}