{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "billing-settings-2",
  "title": "Billing Settings 2",
  "description": "A comprehensive billing settings component with tabs for general, payment methods, invoices, and usage limits",
  "dependencies": ["currency-codes"],
  "registryDependencies": [
    "button",
    "card",
    "input",
    "label",
    "select",
    "switch",
    "utils"
  ],
  "files": [
    {
      "path": "src/registry/billingsdk/billing-settings-2.tsx",
      "content": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\nimport { Switch } from \"@/components/ui/switch\";\nimport { cn } from \"@/lib/utils\";\nimport { useState, useMemo } from \"react\";\nimport currencyCodes from \"currency-codes\";\n\n// Define types for our props\nexport interface FeatureToggle {\n  id: string;\n  label: string;\n  description: string;\n  enabled: boolean;\n  onToggle: (enabled: boolean) => void;\n}\n\nexport interface InputField {\n  id: string;\n  name: string;\n  value?: string;\n  defaultValue?: string;\n  placeholder: string;\n  onChange: (value: string) => void;\n  label: string;\n  helperText?: string;\n  type?: \"text\" | \"email\" | \"tel\" | \"url\" | \"number\";\n  required?: boolean;\n  validation?: {\n    minLength?: number;\n    maxLength?: number;\n    pattern?: RegExp;\n    customValidator?: (value: string) => string | null;\n  };\n}\n\nexport interface ValidationError {\n  field: string;\n  message: string;\n}\n\nexport interface BillingSettings2Props {\n  className?: string;\n  title?: string;\n  features?: FeatureToggle[];\n  inputFields?: InputField[];\n  onSave?: () => void;\n  onCancel?: () => void;\n  saveButtonText?: string;\n  cancelButtonText?: string;\n  currencies?: string[]; // Array of currency codes to show (e.g., ['USD', 'EUR', 'GBP'])\n  currencyOptions?: { value: string; label: string }[]; // Override for custom currency options\n  defaultCurrency?: string;\n  onCurrencyChange?: (value: string) => void;\n  enableValidation?: boolean;\n  currencyRequired?: boolean;\n}\n\n// Validation helper functions\nconst validateEmail = (email: string): string | null => {\n  const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n  if (!emailRegex.test(email)) {\n    return \"Please enter a valid email address\";\n  }\n  return null;\n};\n\nconst validateField = (value: string, field: InputField): string | null => {\n  // Check required validation\n  if (field.required && !value.trim()) {\n    return `${field.label} is required`;\n  }\n\n  // Skip other validations if field is empty and not required\n  if (!value.trim() && !field.required) {\n    return null;\n  }\n\n  // Check email validation\n  if (field.type === \"email\") {\n    return validateEmail(value);\n  }\n\n  // Check custom validation\n  if (field.validation?.customValidator) {\n    return field.validation.customValidator(value);\n  }\n\n  // Check pattern validation\n  if (field.validation?.pattern && !field.validation.pattern.test(value)) {\n    return `Invalid ${field.label.toLowerCase()} format`;\n  }\n\n  // Check length validations\n  if (\n    field.validation?.minLength &&\n    value.length < field.validation.minLength\n  ) {\n    return `${field.label} must be at least ${field.validation.minLength} characters`;\n  }\n\n  if (\n    field.validation?.maxLength &&\n    value.length > field.validation.maxLength\n  ) {\n    return `${field.label} must be no more than ${field.validation.maxLength} characters`;\n  }\n\n  return null;\n};\n\nexport function BillingSettings2({\n  className,\n  title = \"Billing Settings\",\n  features = [\n    {\n      id: \"auto-renewal\",\n      label: \"Auto-Renewal\",\n      description: \"Automatically renew your subscription\",\n      enabled: true,\n      onToggle: () => {},\n    },\n    {\n      id: \"invoice-emails\",\n      label: \"Invoice Emails\",\n      description: \"Receive emails when invoices are generated\",\n      enabled: true,\n      onToggle: () => {},\n    },\n    {\n      id: \"promotional-emails\",\n      label: \"Promotional Emails\",\n      description: \"Receive occasional updates about new features and offers\",\n      enabled: true,\n      onToggle: () => {},\n    },\n  ],\n  inputFields = [\n    {\n      id: \"fullName\",\n      name: \"fullName\",\n      defaultValue: \"\",\n      placeholder: \"John Doe\",\n      onChange: () => {},\n      label: \"Full Name\",\n      type: \"text\",\n      required: true,\n    },\n    {\n      id: \"billingEmail\",\n      name: \"billingEmail\",\n      defaultValue: \"\",\n      placeholder: \"user@example.com\",\n      onChange: () => {},\n      label: \"Billing Email\",\n      helperText: \"Invoices will be sent to this email address\",\n      type: \"email\",\n      required: true,\n    },\n    {\n      id: \"taxId\",\n      name: \"taxId\",\n      defaultValue: \"\",\n      placeholder: \"EU123456789\",\n      onChange: () => {},\n      label: \"Tax ID (Optional)\",\n      helperText: \"For VAT or other tax purposes\",\n      type: \"text\",\n    },\n  ],\n  onSave = () => {},\n  onCancel = () => {},\n  saveButtonText = \"Save Changes\",\n  cancelButtonText = \"Cancel\",\n  currencies, // Array of specific currency codes to show\n  currencyOptions, // Custom currency options override\n  defaultCurrency = \"USD\",\n  onCurrencyChange = () => {},\n  enableValidation = true,\n  currencyRequired = true,\n}: BillingSettings2Props) {\n  const [validationErrors, setValidationErrors] = useState<ValidationError[]>(\n    [],\n  );\n  const [currencyError, setCurrencyError] = useState<string | null>(null);\n\n  // Generate currency options from currency-codes package\n  const generatedCurrencyOptions = useMemo(() => {\n    // If custom currencyOptions are provided, use them\n    if (currencyOptions) {\n      return currencyOptions;\n    }\n\n    // Get all currency data\n    const allCurrencies = currencyCodes.data;\n\n    // If specific currencies are requested, filter to those\n    if (currencies && currencies.length > 0) {\n      return currencies\n        .map((code) => {\n          const currency = allCurrencies.find(\n            (c) => c.code === code.toUpperCase(),\n          );\n          return currency\n            ? {\n                value: currency.code.toLowerCase(),\n                label: `${currency.code} - ${currency.currency}`,\n              }\n            : null;\n        })\n        .filter(Boolean) as { value: string; label: string }[];\n    }\n\n    // Return all currencies if no specific ones requested\n    return allCurrencies\n      .filter((currency) => currency.code && currency.currency) // Filter out invalid entries\n      .map((currency) => ({\n        value: currency.code.toLowerCase(),\n        label: `${currency.code} - ${currency.currency}`,\n      }))\n      .sort((a, b) => a.label.localeCompare(b.label)); // Sort alphabetically\n  }, [currencies, currencyOptions]);\n\n  // Normalize the defaultCurrency to lowercase to match generated options\n  const normalizedDefaultCurrency = defaultCurrency?.toLowerCase();\n\n  // Validate all fields\n  const validateAllFields = (): boolean => {\n    if (!enableValidation) return true;\n\n    const errors: ValidationError[] = [];\n    let hasCurrencyError = false;\n\n    // Validate input fields\n    inputFields.forEach((field) => {\n      const value =\n        field.value !== undefined ? field.value : field.defaultValue || \"\";\n      const error = validateField(value, field);\n      if (error) {\n        errors.push({ field: field.id, message: error });\n      }\n    });\n\n    // Validate currency if required\n    if (currencyRequired && !normalizedDefaultCurrency) {\n      setCurrencyError(\"Please select a currency\");\n      hasCurrencyError = true;\n    } else {\n      setCurrencyError(null);\n    }\n\n    setValidationErrors(errors);\n    return errors.length === 0 && !hasCurrencyError;\n  };\n\n  // Handle save with validation\n  const handleSave = () => {\n    if (validateAllFields()) {\n      onSave();\n    }\n  };\n\n  // Get error for a specific field\n  const getFieldError = (fieldId: string): string | undefined => {\n    return validationErrors.find((error) => error.field === fieldId)?.message;\n  };\n\n  // Clear validation error for a specific field\n  const clearFieldError = (fieldId: string) => {\n    setValidationErrors((prev) =>\n      prev.filter((error) => error.field !== fieldId),\n    );\n  };\n\n  // Clear currency error\n  const clearCurrencyError = () => {\n    setCurrencyError(null);\n  };\n\n  // Enhanced input change handler that clears validation errors\n  const handleInputChange = (\n    fieldId: string,\n    value: string,\n    originalOnChange: (value: string) => void,\n  ) => {\n    // Clear the validation error for this field when user starts typing\n    clearFieldError(fieldId);\n    // Call the original onChange handler\n    originalOnChange(value);\n  };\n\n  // Enhanced currency change handler\n  const handleCurrencyChange = (\n    value: string,\n    originalOnChange: (value: string) => void,\n  ) => {\n    // Clear currency error when user makes a selection\n    clearCurrencyError();\n    // Call the original onChange handler\n    originalOnChange(value);\n  };\n  return (\n    <Card className={cn(\"mx-auto max-w-2xl\", className)}>\n      <CardHeader>\n        <CardTitle className=\"text-lg\">{title}</CardTitle>\n      </CardHeader>\n      <CardContent className=\"space-y-6\">\n        <p className=\"text-muted-foreground text-sm\">\n          Manage your billing preferences and settings\n        </p>\n\n        <div className=\"grid grid-cols-1 gap-6 md:grid-cols-2\">\n          {inputFields.map((field) => {\n            const error = getFieldError(field.id);\n            return (\n              <div key={field.id} className=\"space-y-2\">\n                <Label htmlFor={field.id}>\n                  {field.label}\n                  {field.required && (\n                    <span className=\"ml-1 text-red-500\">*</span>\n                  )}\n                </Label>\n                <Input\n                  id={field.id}\n                  name={field.name}\n                  {...(field.value !== undefined\n                    ? { value: field.value }\n                    : { defaultValue: field.defaultValue })}\n                  placeholder={field.placeholder}\n                  onChange={(e) =>\n                    handleInputChange(field.id, e.target.value, field.onChange)\n                  }\n                  type={field.type || \"text\"}\n                  aria-describedby={\n                    field.helperText ? `${field.id}-help` : undefined\n                  }\n                  className={error ? \"border-red-500 focus:border-red-500\" : \"\"}\n                />\n                {error ? (\n                  <p className=\"text-xs text-red-500\">{error}</p>\n                ) : field.helperText ? (\n                  <p\n                    id={`${field.id}-help`}\n                    className=\"text-muted-foreground text-xs\"\n                  >\n                    {field.helperText}\n                  </p>\n                ) : null}\n              </div>\n            );\n          })}\n\n          <div className=\"min-w-0 space-y-2\">\n            <Label id=\"currency-label\">\n              Currency\n              {currencyRequired && <span className=\"ml-1 text-red-500\">*</span>}\n            </Label>\n            <Select\n              value={normalizedDefaultCurrency}\n              onValueChange={(value) =>\n                handleCurrencyChange(value, onCurrencyChange)\n              }\n            >\n              <SelectTrigger\n                aria-labelledby=\"currency-label\"\n                className={cn(\n                  \"w-[280px] flex-shrink-0\",\n                  currencyError ? \"border-red-500 focus:border-red-500\" : \"\",\n                )}\n              >\n                <SelectValue\n                  placeholder=\"Select currency\"\n                  className=\"truncate overflow-hidden text-ellipsis whitespace-nowrap\"\n                />\n              </SelectTrigger>\n              <SelectContent className=\"max-h-[200px] w-[280px]\">\n                {generatedCurrencyOptions.map((option) => (\n                  <SelectItem\n                    key={option.value}\n                    value={option.value}\n                    className=\"w-full\"\n                  >\n                    <span\n                      className=\"block w-full truncate\"\n                      title={option.label}\n                    >\n                      {option.label}\n                    </span>\n                  </SelectItem>\n                ))}\n              </SelectContent>\n            </Select>\n            {currencyError && (\n              <p className=\"text-xs text-red-500\">{currencyError}</p>\n            )}\n          </div>\n        </div>\n\n        <div className=\"space-y-4\">\n          {features.map((feature) => (\n            <div\n              key={feature.id}\n              className=\"flex items-center justify-between rounded-lg border p-4\"\n            >\n              <div>\n                <div className=\"font-medium\">{feature.label}</div>\n                <div className=\"text-muted-foreground text-sm\">\n                  {feature.description}\n                </div>\n              </div>\n              <Switch\n                aria-label={feature.label}\n                checked={feature.enabled}\n                onCheckedChange={feature.onToggle}\n              />\n            </div>\n          ))}\n        </div>\n\n        <div className=\"flex items-center justify-end gap-3 pt-2\">\n          <Button variant=\"outline\" onClick={onCancel}>\n            {cancelButtonText}\n          </Button>\n          <Button\n            onClick={handleSave}\n            disabled={\n              enableValidation &&\n              (validationErrors.length > 0 || !!currencyError)\n            }\n          >\n            {saveButtonText}\n          </Button>\n        </div>\n      </CardContent>\n    </Card>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/billingsdk/billing-settings-2.tsx"
    },
    {
      "path": "src/registry/billingsdk/demo/billing-settings-2-demo.tsx",
      "content": "\"use client\";\n\nimport { BillingSettings2 } from \"@/components/billingsdk/billing-settings-2\";\nimport { useState } from \"react\";\n\nexport function BillingSettings2Demo() {\n  const [inputValues, setInputValues] = useState({\n    fullName: \"\",\n    billingEmail: \"\",\n    taxId: \"\",\n  });\n\n  const [featureToggles, setFeatureToggles] = useState({\n    autoRenewal: true,\n    invoiceEmails: true,\n    promotionalEmails: false,\n  });\n\n  const [selectedCurrency, setSelectedCurrency] = useState(\"usd\");\n\n  const handleInputChange = (field: string, value: string) => {\n    setInputValues((prev) => ({\n      ...prev,\n      [field]: value,\n    }));\n  };\n\n  // Handler wrappers\n  const createInputChangeHandler = (field: string) => (value: string) => {\n    handleInputChange(field, value);\n  };\n\n  const createCurrencyChangeHandler =\n    (setCurrency: (value: string) => void) => (value: string) => {\n      console.log(\"Currency changed to:\", value);\n      setCurrency(value);\n      // Add: updatePricing(value), savePreference(value), etc.\n    };\n\n  const handleFeatureToggle = (feature: string, enabled: boolean) => {\n    setFeatureToggles((prev) => ({\n      ...prev,\n      [feature]: enabled,\n    }));\n  };\n\n  const handleSave = () => {\n    // Validation passed if we reach here\n    alert(\"Settings saved successfully!\");\n    console.log(\"Input values:\", inputValues);\n    console.log(\"Feature toggles:\", featureToggles);\n    console.log(\"Selected currency:\", selectedCurrency);\n  };\n\n  const handleCancel = () => {\n    alert(\"Changes cancelled!\");\n  };\n\n  return (\n    <div className=\"p-6\">\n      <BillingSettings2\n        title=\"Custom Billing Settings\"\n        inputFields={[\n          {\n            id: \"fullName\",\n            name: \"fullName\",\n            value: inputValues.fullName,\n            placeholder: \"Enter your full name\",\n            onChange: createInputChangeHandler(\"fullName\"),\n            label: \"Full Name\",\n            type: \"text\",\n            required: true,\n            validation: {\n              minLength: 2,\n              maxLength: 50,\n            },\n          },\n          {\n            id: \"billingEmail\",\n            name: \"billingEmail\",\n            value: inputValues.billingEmail,\n            placeholder: \"user@example.com\",\n            onChange: createInputChangeHandler(\"billingEmail\"),\n            label: \"Billing Email\",\n            helperText: \"Invoices will be sent to this email address\",\n            type: \"email\",\n            required: true,\n          },\n          {\n            id: \"taxId\",\n            name: \"taxId\",\n            value: inputValues.taxId,\n            placeholder: \"EU123456789\",\n            onChange: createInputChangeHandler(\"taxId\"),\n            label: \"Tax ID (Optional)\",\n            helperText: \"For VAT or other tax purposes\",\n            type: \"text\",\n            validation: {\n              pattern: /^[A-Z]{2}\\d{8,12}$/,\n              customValidator: (value: string) => {\n                if (value && !/^[A-Z]{2}\\d{8,12}$/.test(value)) {\n                  return \"Tax ID should be in format: XX followed by 8-12 digits (e.g., EU123456789)\";\n                }\n                return null;\n              },\n            },\n          },\n        ]}\n        features={[\n          {\n            id: \"auto-renewal\",\n            label: \"Auto-Renewal\",\n            description: \"Automatically renew your subscription\",\n            enabled: featureToggles.autoRenewal,\n            onToggle: (enabled) => handleFeatureToggle(\"autoRenewal\", enabled),\n          },\n          {\n            id: \"invoice-emails\",\n            label: \"Invoice Emails\",\n            description: \"Receive emails when invoices are generated\",\n            enabled: featureToggles.invoiceEmails,\n            onToggle: (enabled) =>\n              handleFeatureToggle(\"invoiceEmails\", enabled),\n          },\n          {\n            id: \"promotional-emails\",\n            label: \"Promotional Emails\",\n            description:\n              \"Receive occasional updates about new features and offers\",\n            enabled: featureToggles.promotionalEmails,\n            onToggle: (enabled) =>\n              handleFeatureToggle(\"promotionalEmails\", enabled),\n          },\n        ]}\n        // CURRENCY: Shows all 180+ currencies by default\n        // currencies={['USD', 'EUR', 'GBP']} // Specific currencies\n        // currencyOptions={[{value: 'btc', label: 'Bitcoin'}]} // Custom\n\n        defaultCurrency={selectedCurrency}\n        onCurrencyChange={createCurrencyChangeHandler(setSelectedCurrency)}\n        onSave={handleSave}\n        onCancel={handleCancel}\n        saveButtonText=\"Save Preferences\"\n        cancelButtonText=\"Discard Changes\"\n      />\n\n      {/* \n\t\t\tUSAGE EXAMPLES:\n\t\t\t- Specific: currencies={['USD', 'EUR']}\n\t\t\t- Custom: currencyOptions={[{value: 'btc', label: 'Bitcoin'}]}\n\t\t\t- All: (no currency props) = 180+ currencies\n\t\t\t- Handler: onCurrencyChange={(c) => updatePricing(c)}\n\t\t\t*/}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/billing-settings-2-demo.tsx"
    }
  ],
  "type": "registry:block"
}
