{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "currency-converter-card-shadcnui",
  "type": "registry:component",
  "title": "Currency Converter Card",
  "description": "Finance conversion widget with animated inputs, simulated exchange updates, and contextual feedback",
  "registryDependencies": [
    "button",
    "card"
  ],
  "dependencies": [
    "framer-motion",
    "react"
  ],
  "files": [
    {
      "path": "@uitripled/react-shadcn/src/components/sections/currency-converter-card.tsx",
      "content": "\"use client\";\n\nimport { motion } from \"framer-motion\";\nimport { ArrowLeftRight, TrendingUp } from \"lucide-react\";\nimport { useEffect, useMemo, useState } from \"react\";\n\ntype Currency = {\n  code: string;\n  name: string;\n  symbol: string;\n};\n\ntype CurrencyCode = (typeof CURRENCIES)[number][\"code\"];\n\nconst CURRENCIES: Currency[] = [\n  { code: \"USD\", name: \"US Dollar\", symbol: \"$\" },\n  { code: \"EUR\", name: \"Euro\", symbol: \"€\" },\n  { code: \"GBP\", name: \"British Pound\", symbol: \"£\" },\n  { code: \"JPY\", name: \"Japanese Yen\", symbol: \"¥\" },\n  { code: \"AUD\", name: \"Australian Dollar\", symbol: \"A$\" },\n  { code: \"CAD\", name: \"Canadian Dollar\", symbol: \"C$\" },\n  { code: \"CHF\", name: \"Swiss Franc\", symbol: \"CHF\" },\n  { code: \"CNY\", name: \"Chinese Yuan\", symbol: \"¥\" },\n  { code: \"INR\", name: \"Indian Rupee\", symbol: \"₹\" },\n  { code: \"MXN\", name: \"Mexican Peso\", symbol: \"$\" },\n];\n\nconst BASE_INDEX: Record<CurrencyCode, number> = {\n  USD: 1,\n  EUR: 0.92,\n  GBP: 0.78,\n  JPY: 147.42,\n  AUD: 1.5,\n  CAD: 1.36,\n  CHF: 0.88,\n  CNY: 7.11,\n  INR: 83.24,\n  MXN: 17.12,\n};\n\nexport function CurrencyConverterCard() {\n  const [amount, setAmount] = useState<string>(\"100\");\n  const [fromCurrency, setFromCurrency] = useState<CurrencyCode>(\"USD\");\n  const [toCurrency, setToCurrency] = useState<CurrencyCode>(\"EUR\");\n  const [result, setResult] = useState<number | null>(null);\n  const [rate, setRate] = useState<number | null>(null);\n  const [loading, setLoading] = useState(false);\n  const [isFlipped, setIsFlipped] = useState(false);\n  const [error, setError] = useState<string>(\"\");\n\n  useEffect(() => {\n    const trimmed = amount.trim();\n\n    if (!trimmed) {\n      setLoading(false);\n      setError(\"\");\n      setRate(null);\n      setResult(null);\n      return;\n    }\n\n    const numericAmount = Number(trimmed);\n\n    if (Number.isNaN(numericAmount)) {\n      setLoading(false);\n      setError(\"Enter a valid amount\");\n      setRate(null);\n      setResult(null);\n      return;\n    }\n\n    let cancelled = false;\n\n    setLoading(true);\n    setError(\"\");\n\n    const timeout = window.setTimeout(() => {\n      if (cancelled) return;\n\n      const fromIndex = BASE_INDEX[fromCurrency];\n      const toIndex = BASE_INDEX[toCurrency];\n\n      if (!fromIndex || !toIndex) {\n        setError(\"Unsupported currency selection\");\n        setLoading(false);\n        return;\n      }\n\n      const nextRate = toIndex / fromIndex;\n\n      setRate(nextRate);\n      setResult(numericAmount * nextRate);\n      setIsFlipped((previous) => !previous);\n      setLoading(false);\n    }, 220);\n\n    return () => {\n      cancelled = true;\n      window.clearTimeout(timeout);\n    };\n  }, [amount, fromCurrency, toCurrency]);\n\n  const formattedResult = useMemo(() => {\n    if (result === null) return \"0.00\";\n\n    return result.toLocaleString(undefined, {\n      minimumFractionDigits: 2,\n      maximumFractionDigits: 2,\n    });\n  }, [result]);\n\n  const activeRate = useMemo(() => {\n    if (!rate) return null;\n\n    return rate.toFixed(4);\n  }, [rate]);\n\n  const handleSwap = () => {\n    setFromCurrency(toCurrency);\n    setToCurrency(fromCurrency);\n  };\n\n  const amountSymbol =\n    CURRENCIES.find((currency) => currency.code === fromCurrency)?.symbol ??\n    \"$\";\n  const resultSymbol =\n    CURRENCIES.find((currency) => currency.code === toCurrency)?.symbol ?? \"$\";\n\n  return (\n    <motion.div\n      initial={{ opacity: 0, y: 24 }}\n      animate={{ opacity: 1, y: 0 }}\n      transition={{ duration: 0.4, ease: \"easeOut\" }}\n      className=\"group mx-auto w-full relative\"\n    >\n      <div className=\"absolute inset-0 bg-gradient-to-br from-foreground/[0.04] via-transparent to-transparent opacity-0 transition-opacity duration-300 group-hover:opacity-100 -z-10  rounded-2xl\" />\n      <div className=\"relative overflow-hidden border border-border/60 bg-card/80 backdrop-blur  rounded-2xl\">\n        <div className=\"space-y-1 px-6 pt-6 pb-4\">\n          <h2 className=\"flex items-center gap-2 text-2xl font-semibold text-foreground\">\n            <TrendingUp className=\"h-6 w-6 text-primary\" />\n            Currency Converter\n          </h2>\n          <p className=\"text-sm text-muted-foreground\">\n            Simulated real-time exchange experience\n          </p>\n        </div>\n\n        <div className=\"space-y-6 px-6 pb-6\">\n          <div className=\"space-y-2\">\n            <label className=\"text-sm font-medium text-muted-foreground\">\n              From\n            </label>\n            <div className=\"flex gap-3\">\n              <div className=\"relative flex-1\">\n                <span className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-sm font-medium text-muted-foreground\">\n                  {amountSymbol}\n                </span>\n                <input\n                  type=\"number\"\n                  inputMode=\"decimal\"\n                  value={amount}\n                  onChange={(event) => setAmount(event.target.value)}\n                  placeholder=\"Amount\"\n                  className=\"w-full rounded-lg border border-border bg-background/70 px-8 py-3 text-lg font-semibold text-foreground shadow-sm outline-none transition focus-visible:ring-2 focus-visible:ring-primary/40\"\n                />\n              </div>\n              <select\n                value={fromCurrency}\n                onChange={(event) =>\n                  setFromCurrency(event.target.value as CurrencyCode)\n                }\n                className=\"w-[132px] rounded-lg border border-border bg-background px-3 py-3 text-sm font-semibold text-foreground shadow-sm transition focus-visible:ring-2 focus-visible:ring-primary/40\"\n              >\n                {CURRENCIES.map((currency) => (\n                  <option key={currency.code} value={currency.code}>\n                    {currency.symbol} {currency.code}\n                  </option>\n                ))}\n              </select>\n            </div>\n          </div>\n\n          <div className=\"flex justify-center\">\n            <motion.button\n              type=\"button\"\n              whileHover={{ scale: 1.06 }}\n              whileTap={{ scale: 0.94 }}\n              onClick={handleSwap}\n              disabled={loading}\n              className=\"flex h-12 w-12 items-center justify-center rounded-full border border-border/70 bg-background/50 text-foreground transition hover:bg-background/70 disabled:cursor-not-allowed disabled:opacity-50\"\n            >\n              <ArrowLeftRight className=\"h-5 w-5\" />\n            </motion.button>\n          </div>\n\n          <div className=\"space-y-2\">\n            <label className=\"text-sm font-medium text-muted-foreground\">\n              To\n            </label>\n            <div className=\"flex gap-3\">\n              <motion.div\n                key={isFlipped ? \"flipped\" : \"stationary\"}\n                initial={{ rotateX: 90, opacity: 0 }}\n                animate={{ rotateX: 0, opacity: 1 }}\n                transition={{ duration: 0.3, ease: \"easeOut\" }}\n                className=\"relative flex-1\"\n              >\n                <span className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-sm font-medium text-muted-foreground\">\n                  {resultSymbol}\n                </span>\n                <input\n                  type=\"text\"\n                  value={formattedResult}\n                  readOnly\n                  className=\"w-full rounded-lg border border-border bg-background/60 px-8 py-3 text-lg font-semibold text-foreground shadow-sm outline-none transition focus-visible:ring-2 focus-visible:ring-primary/40\"\n                />\n              </motion.div>\n              <select\n                value={toCurrency}\n                onChange={(event) =>\n                  setToCurrency(event.target.value as CurrencyCode)\n                }\n                className=\"w-[132px] rounded-lg border border-border bg-background px-3 py-3 text-sm font-semibold text-foreground shadow-sm transition focus-visible:ring-2 focus-visible:ring-primary/40\"\n              >\n                {CURRENCIES.map((currency) => (\n                  <option key={currency.code} value={currency.code}>\n                    {currency.symbol} {currency.code}\n                  </option>\n                ))}\n              </select>\n            </div>\n          </div>\n\n          {loading && (\n            <motion.div\n              initial={{ opacity: 0 }}\n              animate={{ opacity: 1 }}\n              className=\"flex items-center justify-center gap-2 rounded-lg border border-border bg-background/60 px-4 py-3 text-sm text-muted-foreground\"\n            >\n              <motion.span\n                className=\"h-4 w-4 rounded-full border-2 border-muted-foreground/60 border-t-transparent\"\n                animate={{ rotate: 360 }}\n                transition={{ repeat: Infinity, duration: 0.8, ease: \"linear\" }}\n              />\n              Calculating latest rates...\n            </motion.div>\n          )}\n\n          {!loading && activeRate && (\n            <motion.div\n              initial={{ opacity: 0 }}\n              animate={{ opacity: 1 }}\n              className=\"rounded-lg border border-primary/20 bg-primary/5 px-4 py-3 text-center text-sm font-medium text-primary\"\n            >\n              1 {fromCurrency} ≈ {activeRate} {toCurrency}\n            </motion.div>\n          )}\n\n          {error && (\n            <motion.div\n              initial={{ opacity: 0 }}\n              animate={{ opacity: 1 }}\n              className=\"rounded-lg border border-destructive/30 bg-destructive/10 px-4 py-3 text-center text-sm font-medium text-destructive\"\n            >\n              {error}\n            </motion.div>\n          )}\n\n          {!loading && !error && (\n            <p className=\"text-center text-xs text-muted-foreground\">\n              Rates are approximated for demo purposes and refresh with each\n              change.\n            </p>\n          )}\n        </div>\n      </div>\n    </motion.div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/uitripled/currency-converter-card-shadcnui.tsx"
    }
  ]
}