{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "upstash-cache-badge",
  "title": "Upstash Cache Badge",
  "description": "Inline cache status indicator showing HIT/MISS/STALE/EXPIRED states with TTL countdown.",
  "dependencies": [
    "@upstash/redis"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/upstash/upstash-cache-badge/components/elements/upstash-cache-badge.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\ntype CacheStatus = \"hit\" | \"miss\" | \"stale\" | \"expired\";\n\ninterface UpstashCacheBadgeProps {\n  status: CacheStatus;\n  ttl?: number;\n  showTtl?: boolean;\n  onRefresh?: () => void;\n  size?: \"sm\" | \"md\" | \"lg\";\n  className?: string;\n}\n\nfunction ZapIcon({ className }: { className?: string }) {\n  return (\n    <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\">\n      <path d=\"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z\" />\n    </svg>\n  );\n}\n\nfunction CloudOffIcon({ className }: { className?: string }) {\n  return (\n    <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\">\n      <path d=\"m2 2 20 20\" />\n      <path d=\"M5.782 5.782A7 7 0 0 0 9 19h8.5a4.5 4.5 0 0 0 1.307-.193\" />\n      <path d=\"M21.532 16.5A4.5 4.5 0 0 0 17.5 10h-1.79A7 7 0 0 0 8 5.789\" />\n    </svg>\n  );\n}\n\nfunction ClockIcon({ className }: { className?: string }) {\n  return (\n    <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\">\n      <circle cx=\"12\" cy=\"12\" r=\"10\" />\n      <polyline points=\"12 6 12 12 16 14\" />\n    </svg>\n  );\n}\n\nfunction RefreshIcon({ className }: { className?: string }) {\n  return (\n    <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\">\n      <path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\" />\n      <path d=\"M21 3v5h-5\" />\n      <path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\" />\n      <path d=\"M8 16H3v5\" />\n    </svg>\n  );\n}\n\nfunction AlertIcon({ className }: { className?: string }) {\n  return (\n    <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\">\n      <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n      <path d=\"M12 9v4\" />\n      <path d=\"M12 17h.01\" />\n    </svg>\n  );\n}\n\nconst STATUS_CONFIG: Record<CacheStatus, { icon: React.ComponentType<{ className?: string }>; color: string; bg: string; label: string }> = {\n  hit: { icon: ZapIcon, color: \"text-green-500\", bg: \"bg-green-500/10 border-green-500/20\", label: \"HIT\" },\n  miss: { icon: CloudOffIcon, color: \"text-orange-500\", bg: \"bg-orange-500/10 border-orange-500/20\", label: \"MISS\" },\n  stale: { icon: ClockIcon, color: \"text-yellow-500\", bg: \"bg-yellow-500/10 border-yellow-500/20\", label: \"STALE\" },\n  expired: { icon: AlertIcon, color: \"text-destructive\", bg: \"bg-destructive/10 border-destructive/20\", label: \"EXPIRED\" },\n};\n\nconst SIZE_CLASSES = {\n  sm: { badge: \"px-2 h-6 text-xs gap-1\", icon: \"w-3 h-3\", ttl: \"text-[10px]\" },\n  md: { badge: \"px-2.5 h-8 text-sm gap-1.5\", icon: \"w-3.5 h-3.5\", ttl: \"text-xs\" },\n  lg: { badge: \"px-3 h-10 text-base gap-2\", icon: \"w-4 h-4\", ttl: \"text-sm\" },\n};\n\nfunction formatTtl(seconds: number): string {\n  if (seconds <= 0) return \"0s\";\n  if (seconds < 60) return `${seconds}s`;\n  if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;\n  if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`;\n  return `${Math.floor(seconds / 86400)}d`;\n}\n\nexport function UpstashCacheBadge({\n  status,\n  ttl,\n  showTtl = true,\n  onRefresh,\n  size = \"md\",\n  className,\n}: UpstashCacheBadgeProps) {\n  const [remainingTtl, setRemainingTtl] = useState(ttl);\n  const config = STATUS_CONFIG[status];\n  const sizes = SIZE_CLASSES[size];\n  const StatusIcon = config.icon;\n\n  useEffect(() => {\n    if (!ttl || !showTtl || status !== \"hit\") return;\n\n    setRemainingTtl(ttl);\n    const interval = setInterval(() => {\n      setRemainingTtl((prev) => {\n        if (!prev || prev <= 1) {\n          clearInterval(interval);\n          return 0;\n        }\n        return prev - 1;\n      });\n    }, 1000);\n\n    return () => clearInterval(interval);\n  }, [ttl, showTtl, status]);\n\n  return (\n    <div\n      data-slot=\"upstash-cache-badge\"\n      className={cn(\"inline-flex items-center\", className)}\n    >\n      <div\n        className={cn(\n          \"inline-flex items-center rounded-l-md border font-mono font-medium\",\n          sizes.badge,\n          config.bg,\n          config.color\n        )}\n      >\n        <StatusIcon className={sizes.icon} />\n        <span>{config.label}</span>\n      </div>\n\n      {showTtl && remainingTtl !== undefined && remainingTtl > 0 && status === \"hit\" && (\n        <div\n          className={cn(\n            \"inline-flex items-center border border-l-0 rounded-r-md bg-muted/50 text-muted-foreground font-mono\",\n            sizes.badge\n          )}\n        >\n          <span className={sizes.ttl}>TTL: {formatTtl(remainingTtl)}</span>\n        </div>\n      )}\n\n      {onRefresh && (status === \"miss\" || status === \"stale\" || status === \"expired\") && (\n        <button\n          type=\"button\"\n          onClick={onRefresh}\n          className={cn(\n            \"inline-flex items-center border border-l-0 rounded-r-md\",\n            \"bg-muted/50 text-muted-foreground hover:text-foreground hover:bg-muted transition-colors\",\n            sizes.badge\n          )}\n        >\n          <RefreshIcon className={sizes.icon} />\n        </button>\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "docs": "Display cache status with color-coded badges. Shows TTL countdown for hits and optional refresh button for misses. Great for debugging cache behavior.",
  "categories": [
    "upstash"
  ],
  "type": "registry:block"
}